History

Tighten platform sdk public surface

Rhys Sullivan committed 7 months ago

Commit 8eaebce1 parent b7b678541 files · +1848 −687Browse files

Unified · Split

41 files
  1. apps/executor/package.json +1 −0
  2. apps/executor/src/cli/main.ts +18 −13
  3. packages/clients/react/package.json +1 −0
  4. packages/clients/react/src/index.test.tsx +11 −5
  5. packages/platform/api/src/executions/api.ts +2 −2
  6. packages/platform/api/src/executions/http.ts +4 −25
  7. packages/platform/api/src/executor-context.ts +35 −0
  8. packages/platform/api/src/http.ts +11 −10
  9. packages/platform/api/src/index.ts +1 −0
  10. packages/platform/api/src/local-context.ts +6 −25
  11. packages/platform/api/src/local/api.ts +4 −3
  12. packages/platform/api/src/local/http.ts +51 −17
  13. packages/platform/api/src/oauth/http.ts +57 −57
  14. packages/platform/api/src/policies/api.ts +2 −2
  15. packages/platform/api/src/policies/http.ts +9 −29
  16. packages/platform/api/src/sources/api.ts +2 −2
  17. packages/platform/api/src/sources/http.ts +251 −239
  18. packages/platform/internal/src/tools/executor-admin.ts +309 −26
  19. packages/platform/sdk/package.json +5 −11
  20. packages/platform/sdk/src/backend.ts +42 −0
  21. packages/platform/sdk/src/contracts.ts +4 −0
  22. packages/platform/sdk/src/executor.ts +694 −0
  23. packages/platform/sdk/src/index.ts +2 −12
  24. packages/platform/sdk/src/operations.ts +6 −0
  25. packages/platform/sdk/src/runtime/control-plane-runtime.test.ts +3 −3
  26. packages/platform/sdk/src/runtime/execution/http.test.ts +1 −3
  27. packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts +1 −1
  28. packages/platform/sdk/src/runtime/execution/runtime-config.test.ts +1 −3
  29. packages/platform/sdk/src/runtime/execution/test-http-client.ts +12 −2
  30. packages/platform/sdk/src/runtime/execution/workspace/environment.ts +6 −0
  31. packages/platform/sdk/src/runtime/execution/workspace/tool-invoker.ts +8 −0
  32. packages/platform/sdk/src/runtime/index.ts +207 −113
  33. packages/platform/sdk/src/runtime/local/installation.test.ts +1 −1
  34. packages/platform/sdk/src/runtime/local/storage.ts +2 −2
  35. packages/platform/sdk/src/runtime/sources/executor-tools.ts +45 −10
  36. packages/platform/sdk/src/schema/index.ts +0 −1
  37. packages/platform/sdk/src/schema/models/auth/account.ts +0 −29
  38. packages/platform/sdk/src/schema/models/auth/index.ts +0 −2
  39. packages/platform/sdk/src/schema/models/auth/principal.ts +0 −20
  40. packages/platform/server/package.json +1 −0
  41. packages/platform/server/src/index.ts +32 −19
apps/executor/package.json+1−0
@@ -40,6 +40,7 @@ · 39 unmodified lines
40 "@executor/platform-api": "workspace:*",40 "@executor/platform-api": "workspace:*",
41 "@executor/platform-internal": "workspace:*",41 "@executor/platform-internal": "workspace:*",
42 "@executor/platform-sdk": "workspace:*",42 "@executor/platform-sdk": "workspace:*",
43 "@executor/platform-sdk-file": "workspace:*",
43 "@executor/server": "workspace:*",44 "@executor/server": "workspace:*",
44 "effect": "catalog:"45 "effect": "catalog:"
45 },46 },
apps/executor/src/cli/main.ts+18−13
@@ -13,13 +13,16 @@ import { · 12 unmodified lines
13 createControlPlaneClient,13 createControlPlaneClient,
14 type ControlPlaneClient,14 type ControlPlaneClient,
15} from "@executor/platform-api";15} from "@executor/platform-api";
16import { createExecutorAdminToolMap } from "@executor/platform-internal";16import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
17import {17import {
18 EXECUTOR_SOURCES_ADD_HELP_LINES,18 EXECUTOR_SOURCES_ADD_HELP_LINES,
19 RuntimeExecutionResolverService,19 RuntimeExecutionResolverService,
20 createControlPlaneRuntime,
21 type ControlPlaneRuntime,
22} from "@executor/platform-sdk/runtime";20} from "@executor/platform-sdk/runtime";
21import {
22 createExecutorEffect,
23 type Executor,
24} from "@executor/platform-sdk";
25import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
23import {26import {
24 ExecutionIdSchema,27 ExecutionIdSchema,
25 type ExecutionEnvelope,28 type ExecutionEnvelope,
@@ -248,9 +251,9 @@ const formatCatalogUnavailableMessage = (cause: Cause.Cause<unknown>): string => · 222 unmodified lines
248 : `Current workspace catalog unavailable: ${message}`;251 : `Current workspace catalog unavailable: ${message}`;
249};252};
250 253
251const closeRuntime = (runtime: ControlPlaneRuntime) =>254const closeExecutor = (executor: Executor) =>
252 Effect.tryPromise({255 Effect.tryPromise({
253 try: () => runtime.close(),256 try: () => executor.close(),
254 catch: toError,257 catch: toError,
255 }).pipe(Effect.catchAll(() => Effect.void));258 }).pipe(Effect.catchAll(() => Effect.void));
256 259
@@ -275,27 +278,29 @@ const buildRunWorkflowText = ( · 18 unmodified lines
275 278
276const loadRunWorkflowText = (): Effect.Effect<string, Error, never> =>279const loadRunWorkflowText = (): Effect.Effect<string, Error, never> =>
277 Effect.acquireUseRelease(280 Effect.acquireUseRelease(
278 createControlPlaneRuntime({281 createExecutorEffect({
279 localDataDir: DEFAULT_LOCAL_DATA_DIR,282 backend: createLocalExecutorBackend({
280 createInternalToolMap: createExecutorAdminToolMap,283 localDataDir: DEFAULT_LOCAL_DATA_DIR,
284 }),
285 createInternalToolMap: createWorkspaceExecutorAdminToolMap,
281 }).pipe(Effect.mapError(toError)),286 }).pipe(Effect.mapError(toError)),
282 (runtime) =>287 (executor) =>
283 Effect.gen(function* () {288 Effect.gen(function* () {
284 const environment = yield* Effect.gen(function* () {289 const environment = yield* Effect.gen(function* () {
285 const resolveExecutionEnvironment = yield* RuntimeExecutionResolverService;290 const resolveExecutionEnvironment = yield* RuntimeExecutionResolverService;
286 return yield* resolveExecutionEnvironment({291 return yield* resolveExecutionEnvironment({
287 workspaceId: runtime.localInstallation.workspaceId,292 workspaceId: executor.workspaceId,
288 accountId: runtime.localInstallation.accountId,293 accountId: executor.accountId,
289 executionId: ExecutionIdSchema.make("exec_help"),294 executionId: ExecutionIdSchema.make("exec_help"),
290 });295 });
291 }).pipe(296 }).pipe(
292 Effect.provide(runtime.runtimeLayer),297 Effect.provide(executor.runtime.runtimeLayer),
293 Effect.mapError(toError),298 Effect.mapError(toError),
294 );299 );
295 300
296 return yield* buildRunWorkflowText(environment.catalog);301 return yield* buildRunWorkflowText(environment.catalog);
297 }),302 }),
298 closeRuntime,303 closeExecutor,
299 ).pipe(304 ).pipe(
300 Effect.catchAllCause((cause) =>305 Effect.catchAllCause((cause) =>
301 Effect.succeed(306 Effect.succeed(
packages/clients/react/package.json+1−0
@@ -24,6 +24,7 @@ · 23 unmodified lines
24 "@effect/platform": "catalog:",24 "@effect/platform": "catalog:",
25 "@effect/platform-node": "catalog:",25 "@effect/platform-node": "catalog:",
26 "@effect/vitest": "catalog:",26 "@effect/vitest": "catalog:",
27 "@executor/platform-sdk-file": "workspace:*",
27 "@types/jsdom": "^21.1.7",28 "@types/jsdom": "^21.1.7",
28 "@types/node": "catalog:",29 "@types/node": "catalog:",
29 "@types/react": "^19.2.10",30 "@types/react": "^19.2.10",
packages/clients/react/src/index.test.tsx+11−5
@@ -6,10 +6,9 @@ import { describe, expect, it } from "@effect/vitest"; · 5 unmodified lines
6import {6import {
7 createControlPlaneApiLayer,7 createControlPlaneApiLayer,
8} from "@executor/platform-api";8} from "@executor/platform-api";
9import {9import { createExecutorEffect } from "@executor/platform-sdk";
10 createControlPlaneRuntime,10import type { ControlPlaneRuntime } from "@executor/platform-sdk/runtime";
11 type ControlPlaneRuntime,11import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "@executor/platform-sdk-file";
12} from "@executor/platform-sdk/runtime";
13import type {12import type {
14 LocalInstallation,13 LocalInstallation,
15 Source,14 Source,
@@ -110,8 +109,15 @@ const startControlPlaneServer = async (): Promise<ApiServer> => { · 94 unmodified lines
110 const scope = await Effect.runPromise(Scope.make());109 const scope = await Effect.runPromise(Scope.make());
111 110
112 try {111 try {
112 const executor = await Effect.runPromise(
113 createExecutorEffect({
114 backend: {
115 createRuntime: () => Effect.succeed(runtime),
116 },
117 }),
118 );
113 const serverLayer = HttpApiBuilder.serve().pipe(119 const serverLayer = HttpApiBuilder.serve().pipe(
114 Layer.provide(createControlPlaneApiLayer(runtime.runtimeLayer)),120 Layer.provide(createControlPlaneApiLayer(executor)),
115 Layer.provideMerge(NodeHttpServer.layerTest),121 Layer.provideMerge(NodeHttpServer.layerTest),
116 );122 );
117 const context = await Effect.runPromise(Layer.buildWithScope(serverLayer, scope));123 const context = await Effect.runPromise(Layer.buildWithScope(serverLayer, scope));
packages/platform/api/src/executions/api.ts+2−2
@@ -2,11 +2,11 @@ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; · 1 unmodified line
2import {2import {
3 CreateExecutionPayloadSchema,3 CreateExecutionPayloadSchema,
4 ResumeExecutionPayloadSchema,4 ResumeExecutionPayloadSchema,
5} from "@executor/platform-sdk/executions/contracts";5} from "@executor/platform-sdk/contracts";
6export type {6export type {
7 CreateExecutionPayload,7 CreateExecutionPayload,
8 ResumeExecutionPayload,8 ResumeExecutionPayload,
9} from "@executor/platform-sdk/executions/contracts";9} from "@executor/platform-sdk/contracts";
10import {10import {
11 ExecutionIdSchema,11 ExecutionIdSchema,
12 ExecutionEnvelopeSchema,12 ExecutionEnvelopeSchema,
packages/platform/api/src/executions/http.ts+4−25
@@ -1,10 +1,5 @@
1import { HttpApiBuilder } from "@effect/platform";1import { HttpApiBuilder } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import {
4 createExecution,
5 getExecution,
6 resumeExecution,
7} from "@executor/platform-sdk/runtime";
8 3
9import { ControlPlaneApi } from "../api";4import { ControlPlaneApi } from "../api";
10import { resolveRequestedLocalWorkspace } from "../local-context";5import { resolveRequestedLocalWorkspace } from "../local-context";
@@ -16,34 +11,18 @@ export const ControlPlaneExecutionsLive = HttpApiBuilder.group( · 5 unmodified lines
16 handlers11 handlers
17 .handle("create", ({ path, payload }) =>12 .handle("create", ({ path, payload }) =>
18 resolveRequestedLocalWorkspace("executions.create", path.workspaceId).pipe(13 resolveRequestedLocalWorkspace("executions.create", path.workspaceId).pipe(
19 Effect.flatMap((runtimeLocalWorkspace) =>14 Effect.flatMap((executor) => executor.effect.executions.create(payload)),
20 createExecution({
21 workspaceId: path.workspaceId,
22 payload,
23 createdByAccountId: runtimeLocalWorkspace.installation.accountId,
24 })
25 ),
26 ),15 ),
27 )16 )
28 .handle("get", ({ path }) =>17 .handle("get", ({ path }) =>
29 resolveRequestedLocalWorkspace("executions.get", path.workspaceId).pipe(18 resolveRequestedLocalWorkspace("executions.get", path.workspaceId).pipe(
30 Effect.zipRight(19 Effect.flatMap((executor) => executor.effect.executions.get(path.executionId)),
31 getExecution({
32 workspaceId: path.workspaceId,
33 executionId: path.executionId,
34 }),
35 ),
36 ),20 ),
37 )21 )
38 .handle("resume", ({ path, payload }) =>22 .handle("resume", ({ path, payload }) =>
39 resolveRequestedLocalWorkspace("executions.resume", path.workspaceId).pipe(23 resolveRequestedLocalWorkspace("executions.resume", path.workspaceId).pipe(
40 Effect.flatMap((runtimeLocalWorkspace) =>24 Effect.flatMap((executor) =>
41 resumeExecution({25 executor.effect.executions.resume(path.executionId, payload)
42 workspaceId: path.workspaceId,
43 executionId: path.executionId,
44 payload,
45 resumedByAccountId: runtimeLocalWorkspace.installation.accountId,
46 })
47 ),26 ),
48 ),27 ),
49 ),28 ),
packages/platform/api/src/executor-context.ts+35−0
@@ -0,0 +1,35 @@
1import type { Executor } from "@executor/platform-sdk";
2import type { WorkspaceId } from "@executor/platform-sdk/schema";
3import {
4 ControlPlaneBadRequestError,
5 ControlPlaneForbiddenError,
6 ControlPlaneUnauthorizedError,
7} from "@executor/platform-sdk/errors";
8import * as Context from "effect/Context";
9import * as Effect from "effect/Effect";
10import * as Layer from "effect/Layer";
11
12export class ControlPlaneExecutor extends Context.Tag(
13 "@executor/platform-api/ControlPlaneExecutor",
14)<ControlPlaneExecutor, Executor>() {}
15
16export const createControlPlaneExecutorLayer = (executor: Executor) =>
17 Layer.succeed(ControlPlaneExecutor, executor);
18
19export const getControlPlaneExecutor = () => ControlPlaneExecutor;
20
21export const resolveRequestedLocalWorkspace = (
22 operation: string,
23 workspaceId: WorkspaceId,
24) =>
25 Effect.flatMap(ControlPlaneExecutor, (executor) =>
26 executor.workspaceId === workspaceId
27 ? Effect.succeed(executor)
28 : Effect.fail(
29 new ControlPlaneForbiddenError({
30 operation,
31 message: "Requested workspace is not the active local workspace",
32 details: `requestedWorkspaceId=${workspaceId} activeWorkspaceId=${executor.workspaceId}`,
33 }),
34 ),
35 );
packages/platform/api/src/http.ts+11−10
@@ -1,7 +1,9 @@
1import { HttpApiBuilder } from "@effect/platform";1import { HttpApiBuilder } from "@effect/platform";
2import * as Layer from "effect/Layer";2import * as Layer from "effect/Layer";
3import type { Executor } from "@executor/platform-sdk";
3 4
4import { ControlPlaneApi } from "./api";5import { ControlPlaneApi } from "./api";
6import { createControlPlaneExecutorLayer } from "./executor-context";
5import { ControlPlaneExecutionsLive } from "./executions/http";7import { ControlPlaneExecutionsLive } from "./executions/http";
6import { ControlPlaneLocalLive } from "./local/http";8import { ControlPlaneLocalLive } from "./local/http";
7import { ControlPlaneOAuthLive } from "./oauth/http";9import { ControlPlaneOAuthLive } from "./oauth/http";
@@ -18,15 +20,14 @@ export const ControlPlaneApiLive = HttpApiBuilder.api(ControlPlaneApi).pipe( · 10 unmodified lines
18 20
19export type ControlPlaneApiRuntimeContext = Layer.Layer.Context<typeof ControlPlaneApiLive>;21export type ControlPlaneApiRuntimeContext = Layer.Layer.Context<typeof ControlPlaneApiLive>;
20 22
21export type BuiltControlPlaneApiLayer = Layer.Layer<23export const createControlPlaneApiLayer = (executor: Executor) =>
22 Layer.Layer.Success<typeof ControlPlaneApiLive>,
23 Layer.Layer.Error<typeof ControlPlaneApiLive>,
24 never
25>;
26
27export const createControlPlaneApiLayer = <ERuntime>(
28 runtimeLayer: Layer.Layer<ControlPlaneApiRuntimeContext, ERuntime, never>,
29) =>
30 ControlPlaneApiLive.pipe(24 ControlPlaneApiLive.pipe(
31 Layer.provide(runtimeLayer),25 Layer.provide(createControlPlaneExecutorLayer(executor)),
32 );26 );
27
28export type BuiltControlPlaneApiLayer = ReturnType<
29 typeof createControlPlaneApiLayer
30>;
31
32export const createExecutorApiLayer = (executor: Executor) =>
33 createControlPlaneApiLayer(executor);
packages/platform/api/src/index.ts+1−0
@@ -22,6 +22,7 @@ export { · 21 unmodified lines
22 type ControlPlaneApiRuntimeContext,22 type ControlPlaneApiRuntimeContext,
23 type BuiltControlPlaneApiLayer,23 type BuiltControlPlaneApiLayer,
24 createControlPlaneApiLayer,24 createControlPlaneApiLayer,
25 createExecutorApiLayer,
25} from "./http";26} from "./http";
26 27
27export {28export {
packages/platform/api/src/local-context.ts+6−25
@@ -1,25 +1,6 @@
1import type { WorkspaceId } from "@executor/platform-sdk/schema";1export {
2import * as Effect from "effect/Effect";2 createControlPlaneExecutorLayer,
3 3 getControlPlaneExecutor,
4import {4 resolveRequestedLocalWorkspace,
5 RuntimeLocalWorkspaceMismatchError,5 type ControlPlaneExecutor,
6 requireRuntimeLocalWorkspace,6} from "./executor-context";
7} from "@executor/platform-sdk/runtime";
8import { ControlPlaneForbiddenError } from "./errors";
9
10export const resolveRequestedLocalWorkspace = (
11 operation: string,
12 workspaceId: WorkspaceId,
13) =>
14 requireRuntimeLocalWorkspace(workspaceId).pipe(
15 Effect.mapError((cause) =>
16 new ControlPlaneForbiddenError({
17 operation,
18 message: "Requested workspace is not the active local workspace",
19 details:
20 cause instanceof RuntimeLocalWorkspaceMismatchError
21 ? `requestedWorkspaceId=${cause.requestedWorkspaceId} activeWorkspaceId=${cause.activeWorkspaceId}`
22 : "Runtime local workspace is unavailable",
23 })
24 ),
25 );
packages/platform/api/src/local/api.ts+4−3
@@ -7,7 +7,7 @@ import { · 6 unmodified lines
7 SecretListItemSchema,7 SecretListItemSchema,
8 UpdateSecretPayloadSchema,8 UpdateSecretPayloadSchema,
9 UpdateSecretResultSchema,9 UpdateSecretResultSchema,
10} from "@executor/platform-sdk/local/contracts";10} from "@executor/platform-sdk/contracts";
11import {11import {
12 ControlPlaneBadRequestError,12 ControlPlaneBadRequestError,
13 ControlPlaneNotFoundError,13 ControlPlaneNotFoundError,
@@ -26,7 +26,7 @@ export type { · 12 unmodified lines
26 SecretProvider,26 SecretProvider,
27 UpdateSecretPayload,27 UpdateSecretPayload,
28 UpdateSecretResult,28 UpdateSecretResult,
29} from "@executor/platform-sdk/local/contracts";29} from "@executor/platform-sdk/contracts";
30 30
31export class LocalApi extends HttpApiGroup.make("local")31export class LocalApi extends HttpApiGroup.make("local")
32 .add(32 .add(
@@ -38,7 +38,8 @@ export class LocalApi extends HttpApiGroup.make("local") · 5 unmodified lines
38 )38 )
39 .add(39 .add(
40 HttpApiEndpoint.get("config")`/local/config`40 HttpApiEndpoint.get("config")`/local/config`
41 .addSuccess(InstanceConfigSchema),41 .addSuccess(InstanceConfigSchema)
42 .addError(ControlPlaneStorageError),
42 )43 )
43 .add(44 .add(
44 HttpApiEndpoint.get("listSecrets")`/local/secrets`45 HttpApiEndpoint.get("listSecrets")`/local/secrets`
packages/platform/api/src/local/http.ts+51−17
@@ -1,29 +1,63 @@
1import { HttpApiBuilder } from "@effect/platform";1import { HttpApiBuilder } from "@effect/platform";
2import {2import * as Effect from "effect/Effect";
3 createLocalSecret,
4 deleteLocalSecret,
5 getLocalInstanceConfig,
6 getLocalInstallation,
7 listLocalSecrets,
8 updateLocalSecret,
9} from "@executor/platform-sdk";
10 3
11import { ControlPlaneApi } from "../api";4import { ControlPlaneApi } from "../api";
5import { ControlPlaneStorageError } from "../errors";
6import { getControlPlaneExecutor } from "../executor-context";
7
8const toStorageError = (operation: string) => (cause: unknown) =>
9 new ControlPlaneStorageError({
10 operation,
11 message: cause instanceof Error ? cause.message : String(cause),
12 details: cause instanceof Error ? cause.stack ?? cause.message : String(cause),
13 });
12 14
13export const ControlPlaneLocalLive = HttpApiBuilder.group(15export const ControlPlaneLocalLive = HttpApiBuilder.group(
14 ControlPlaneApi,16 ControlPlaneApi,
15 "local",17 "local",
16 (handlers) =>18 (handlers) =>
17 handlers19 handlers
18 .handle("installation", () => getLocalInstallation())20 .handle("installation", () =>
19 .handle("config", () => getLocalInstanceConfig())21 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
20 .handle("listSecrets", () => listLocalSecrets())22 executor.effect.local.installation().pipe(
21 .handle("createSecret", ({ payload }) => createLocalSecret(payload))23 Effect.mapError(toStorageError("local.installation")),
24 )
25 )
26 )
27 .handle("config", () =>
28 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
29 executor.effect.local.config().pipe(
30 Effect.mapError(toStorageError("local.config")),
31 )
32 )
33 )
34 .handle("listSecrets", () =>
35 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
36 executor.effect.secrets.list().pipe(
37 Effect.mapError(toStorageError("local.listSecrets")),
38 )
39 )
40 )
41 .handle("createSecret", ({ payload }) =>
42 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
43 executor.effect.secrets.create(payload).pipe(
44 Effect.mapError(toStorageError("local.createSecret")),
45 )
46 )
47 )
22 .handle("updateSecret", ({ path, payload }) =>48 .handle("updateSecret", ({ path, payload }) =>
23 updateLocalSecret({49 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
24 secretId: path.secretId,50 executor.effect.secrets.update({
25 payload,51 secretId: path.secretId,
26 }),52 payload,
53 }).pipe(Effect.mapError(toStorageError("local.updateSecret")))
54 )
27 )55 )
28 .handle("deleteSecret", ({ path }) => deleteLocalSecret(path.secretId)),56 .handle("deleteSecret", ({ path }) =>
57 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
58 executor.effect.secrets.remove(path.secretId).pipe(
59 Effect.mapError(toStorageError("local.deleteSecret")),
60 )
61 )
62 ),
29);63);
packages/platform/api/src/oauth/http.ts+57−57
@@ -9,13 +9,15 @@ import * as Either from "effect/Either"; · 8 unmodified lines
9import * as Effect from "effect/Effect";9import * as Effect from "effect/Effect";
10import * as Schema from "effect/Schema";10import * as Schema from "effect/Schema";
11 11
12import { RuntimeSourceAuthServiceTag } from "@executor/platform-sdk/runtime";
13import { ControlPlaneApi } from "../api";12import { ControlPlaneApi } from "../api";
14import {13import {
15 ControlPlaneBadRequestError,14 ControlPlaneBadRequestError,
16 ControlPlaneStorageError,15 ControlPlaneStorageError,
17} from "../errors";16} from "../errors";
18import { resolveRequestedLocalWorkspace } from "../local-context";17import {
18 getControlPlaneExecutor,
19 resolveRequestedLocalWorkspace,
20} from "../local-context";
19import {21import {
20 SourceOAuthPopupResultSchema,22 SourceOAuthPopupResultSchema,
21 type SourceOAuthPopupResult,23 type SourceOAuthPopupResult,
@@ -236,23 +238,20 @@ export const ControlPlaneOAuthLive = HttpApiBuilder.group( · 214 unmodified lines
236 handlers238 handlers
237 .handle("startSourceAuth", ({ path, payload }) =>239 .handle("startSourceAuth", ({ path, payload }) =>
238 resolveRequestedLocalWorkspace("oauth.start_source_auth", path.workspaceId).pipe(240 resolveRequestedLocalWorkspace("oauth.start_source_auth", path.workspaceId).pipe(
239 Effect.flatMap((runtimeLocalWorkspace) =>241 Effect.flatMap((executor) =>
240 Effect.gen(function* () {242 Effect.gen(function* () {
241 const request = yield* HttpServerRequest.HttpServerRequest;243 const request = yield* HttpServerRequest.HttpServerRequest;
242 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;244 return yield* executor.effect.oauth.startSourceAuth({
243 return yield* sourceAuthService.startSourceOAuthSession({245 baseUrl: resolveRequestOrigin(request),
244 workspaceId: path.workspaceId,246 displayName: payload.name,
245 actorAccountId: runtimeLocalWorkspace.installation.accountId,247 provider: {
246 baseUrl: resolveRequestOrigin(request),248 kind: payload.provider,
247 displayName: payload.name,249 endpoint: payload.endpoint,
248 provider: {250 transport: payload.transport,
249 kind: payload.provider,251 queryParams: payload.queryParams,
250 endpoint: payload.endpoint,252 headers: payload.headers,
251 transport: payload.transport,253 },
252 queryParams: payload.queryParams,254 });
253 headers: payload.headers,
254 },
255 });
256 }).pipe(255 }).pipe(
257 Effect.catchAllCause((cause) =>256 Effect.catchAllCause((cause) =>
258 Effect.fail(257 Effect.fail(
@@ -269,53 +268,54 @@ export const ControlPlaneOAuthLive = HttpApiBuilder.group( · 10 unmodified lines
269 ),268 ),
270 )269 )
271 .handle("sourceAuthCallback", ({ urlParams }) =>270 .handle("sourceAuthCallback", ({ urlParams }) =>
272 Effect.gen(function* () {271 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
273 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;272 Effect.gen(function* () {
274 const completed = yield* Effect.either(273 const completed = yield* Effect.either(
275 sourceAuthService.completeSourceOAuthSession({274 executor.effect.oauth.completeSourceAuth({
276 state: urlParams.state,275 state: urlParams.state,
277 code: urlParams.code,276 code: urlParams.code,
278 error: urlParams.error,277 error: urlParams.error,
279 errorDescription: urlParams.error_description,278 errorDescription: urlParams.error_description,
280 }),279 }),
281 );280 );
281
282 if (Either.isRight(completed)) {
283 return htmlResponse(
284 sourceOAuthPopupResultDocument({
285 title: "OAuth connected",
286 message: "OAuth credentials are ready. Return to the source form to finish saving.",
287 status: "connected",
288 sessionId: completed.right.sessionId,
289 payload: {
290 type: "executor:oauth-result",
291 ok: true,
292 sessionId: completed.right.sessionId,
293 auth: completed.right.auth,
294 },
295 }),
296 );
297 }
298
299 const message = completed.left instanceof Error
300 ? completed.left.message
301 : "Failed completing OAuth";
282 302
283 if (Either.isRight(completed)) {
284 return htmlResponse(303 return htmlResponse(
285 sourceOAuthPopupResultDocument({304 sourceOAuthPopupResultDocument({
286 title: "OAuth connected",305 title: "OAuth failed",
287 message: "OAuth credentials are ready. Return to the source form to finish saving.",306 message,
288 status: "connected",307 status: "failed",
289 sessionId: completed.right.sessionId,308 sessionId: "failed",
290 payload: {309 payload: {
291 type: "executor:oauth-result",310 type: "executor:oauth-result",
292 ok: true,311 ok: false,
293 sessionId: completed.right.sessionId,312 sessionId: null,
294 auth: completed.right.auth,313 error: message,
295 },314 },
296 }),315 }),
316 500,
297 );317 );
298 }318 }),
299 319 ),
300 const message = completed.left instanceof Error
301 ? completed.left.message
302 : "Failed completing OAuth";
303
304 return htmlResponse(
305 sourceOAuthPopupResultDocument({
306 title: "OAuth failed",
307 message,
308 status: "failed",
309 sessionId: "failed",
310 payload: {
311 type: "executor:oauth-result",
312 ok: false,
313 sessionId: null,
314 error: message,
315 },
316 }),
317 500,
318 );
319 }),
320 ),320 ),
321);321);
packages/platform/api/src/policies/api.ts+2−2
@@ -2,7 +2,7 @@ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; · 1 unmodified line
2import {2import {
3 CreatePolicyPayloadSchema,3 CreatePolicyPayloadSchema,
4 UpdatePolicyPayloadSchema,4 UpdatePolicyPayloadSchema,
5} from "@executor/platform-sdk/policies/contracts";5} from "@executor/platform-sdk/contracts";
6import {6import {
7 ControlPlaneBadRequestError,7 ControlPlaneBadRequestError,
8 ControlPlaneForbiddenError,8 ControlPlaneForbiddenError,
@@ -20,7 +20,7 @@ import * as Schema from "effect/Schema"; · 11 unmodified lines
20export type {20export type {
21 CreatePolicyPayload,21 CreatePolicyPayload,
22 UpdatePolicyPayload,22 UpdatePolicyPayload,
23} from "@executor/platform-sdk/policies/contracts";23} from "@executor/platform-sdk/contracts";
24 24
25export {25export {
26 CreatePolicyPayloadSchema,26 CreatePolicyPayloadSchema,
packages/platform/api/src/policies/http.ts+9−29
@@ -1,12 +1,5 @@
1import { HttpApiBuilder } from "@effect/platform";1import { HttpApiBuilder } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import {
4 createPolicy,
5 getPolicy,
6 listPolicies,
7 removePolicy,
8 updatePolicy,
9} from "@executor/platform-sdk";
10 3
11import { ControlPlaneApi } from "../api";4import { ControlPlaneApi } from "../api";
12import { resolveRequestedLocalWorkspace } from "../local-context";5import { resolveRequestedLocalWorkspace } from "../local-context";
@@ -18,7 +11,7 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 5 unmodified lines
18 handlers11 handlers
19 .handle("list", ({ path }) =>12 .handle("list", ({ path }) =>
20 resolveRequestedLocalWorkspace("policies.list", path.workspaceId).pipe(13 resolveRequestedLocalWorkspace("policies.list", path.workspaceId).pipe(
21 Effect.zipRight(listPolicies(path.workspaceId)),14 Effect.flatMap((executor) => executor.effect.policies.list()),
22 ),15 ),
23 )16 )
24 .handle("create", ({ path, payload }) =>17 .handle("create", ({ path, payload }) =>
@@ -26,19 +19,12 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 1 unmodified line
26 "policies.create",19 "policies.create",
27 path.workspaceId,20 path.workspaceId,
28 ).pipe(21 ).pipe(
29 Effect.zipRight(22 Effect.flatMap((executor) => executor.effect.policies.create(payload)),
30 createPolicy({ workspaceId: path.workspaceId, payload }),
31 ),
32 ),23 ),
33 )24 )
34 .handle("get", ({ path }) =>25 .handle("get", ({ path }) =>
35 resolveRequestedLocalWorkspace("policies.get", path.workspaceId).pipe(26 resolveRequestedLocalWorkspace("policies.get", path.workspaceId).pipe(
36 Effect.zipRight(27 Effect.flatMap((executor) => executor.effect.policies.get(path.policyId)),
37 getPolicy({
38 workspaceId: path.workspaceId,
39 policyId: path.policyId,
40 }),
41 ),
42 ),28 ),
43 )29 )
44 .handle("update", ({ path, payload }) =>30 .handle("update", ({ path, payload }) =>
@@ -46,12 +32,8 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 1 unmodified line
46 "policies.update",32 "policies.update",
47 path.workspaceId,33 path.workspaceId,
48 ).pipe(34 ).pipe(
49 Effect.zipRight(35 Effect.flatMap((executor) =>
50 updatePolicy({36 executor.effect.policies.update(path.policyId, payload)
51 workspaceId: path.workspaceId,
52 policyId: path.policyId,
53 payload,
54 }),
55 ),37 ),
56 ),38 ),
57 )39 )
@@ -60,12 +42,10 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 2 unmodified lines
60 "policies.remove",42 "policies.remove",
61 path.workspaceId,43 path.workspaceId,
62 ).pipe(44 ).pipe(
63 Effect.zipRight(45 Effect.flatMap((executor) => executor.effect.policies.remove(path.policyId)),
64 removePolicy({46 Effect.map((result) => ({
65 workspaceId: path.workspaceId,47 removed: result.removed,
66 policyId: path.policyId,48 })),
67 }),
68 ),
69 ),49 ),
70 ),50 ),
71);51);
packages/platform/api/src/sources/api.ts+2−2
@@ -12,7 +12,7 @@ import { · 11 unmodified lines
12 DiscoverSourcePayloadSchema,12 DiscoverSourcePayloadSchema,
13 UpdateSourcePayloadSchema,13 UpdateSourcePayloadSchema,
14 WorkspaceOauthClientQuerySchema,14 WorkspaceOauthClientQuerySchema,
15} from "@executor/platform-sdk/sources/contracts";15} from "@executor/platform-sdk/contracts";
16import {16import {
17 ControlPlaneBadRequestError,17 ControlPlaneBadRequestError,
18 ControlPlaneForbiddenError,18 ControlPlaneForbiddenError,
@@ -44,7 +44,7 @@ export type { · 25 unmodified lines
44 CreateWorkspaceOauthClientPayload,44 CreateWorkspaceOauthClientPayload,
45 DiscoverSourcePayload,45 DiscoverSourcePayload,
46 UpdateSourcePayload,46 UpdateSourcePayload,
47} from "@executor/platform-sdk/sources/contracts";47} from "@executor/platform-sdk/contracts";
48 48
49export {49export {
50 ConnectSourceBatchPayloadSchema,50 ConnectSourceBatchPayloadSchema,
packages/platform/api/src/sources/http.ts+251−239
@@ -7,28 +7,18 @@ import type { · 6 unmodified lines
7 ExecutionInteraction,7 ExecutionInteraction,
8 Source,8 Source,
9 WorkspaceId,9 WorkspaceId,
10 WorkspaceOauthClient,
10} from "@executor/platform-sdk/schema";11} from "@executor/platform-sdk/schema";
12import type {
13 ExecutorMcpSourceInput,
14 ExecutorSourceInput,
15} from "@executor/platform-sdk";
11import * as Effect from "effect/Effect";16import * as Effect from "effect/Effect";
12 17
13import {
14 completeSourceCredentialSetup,
15 createSource,
16 discoverSource,
17 discoverSourceInspectionTools,
18 getSource,
19 getSourceCredentialInteraction,
20 getSourceInspection,
21 getSourceInspectionToolDetail,
22 listSources,
23 removeSource,
24 submitSourceCredentialInteraction,
25 updateSource,
26} from "@executor/platform-sdk";
27import {18import {
28 sourceAdapterRequiresInteractiveConnect,19 sourceAdapterRequiresInteractiveConnect,
29 RuntimeSourceAuthServiceTag,
30 type ExecutorAddSourceInput,
31} from "@executor/platform-sdk/runtime";20} from "@executor/platform-sdk/runtime";
21import { WorkspaceOauthClientIdSchema } from "@executor/platform-sdk/schema";
32 22
33import {23import {
34 ControlPlaneBadRequestError,24 ControlPlaneBadRequestError,
@@ -37,7 +27,10 @@ import { · 2 unmodified lines
37} from "../errors";27} from "../errors";
38import { ControlPlaneApi } from "../api";28import { ControlPlaneApi } from "../api";
39import type { ConnectSourcePayload } from "./api";29import type { ConnectSourcePayload } from "./api";
40import { resolveRequestedLocalWorkspace } from "../local-context";30import {
31 getControlPlaneExecutor,
32 resolveRequestedLocalWorkspace,
33} from "../local-context";
41 34
42const readHeader = (headers: unknown, name: string): string | null => {35const readHeader = (headers: unknown, name: string): string | null => {
43 if (headers == null || typeof headers !== "object") {36 if (headers == null || typeof headers !== "object") {
@@ -93,6 +86,80 @@ const isInteractiveConnectPayload = ( · 49 unmodified lines
93 payload.kind === undefined ||86 payload.kind === undefined ||
94 sourceAdapterRequiresInteractiveConnect(payload.kind);87 sourceAdapterRequiresInteractiveConnect(payload.kind);
95 88
89const nullToUndefined = <T>(value: T | null | undefined): T | undefined =>
90 value ?? undefined;
91
92const toWorkspaceOauthClientId = (
93 value: string | null | undefined,
94): WorkspaceOauthClient["id"] | null | undefined =>
95 value == null ? value : WorkspaceOauthClientIdSchema.make(value);
96
97const toExecutorSourceInput = (
98 payload: ConnectSourcePayload,
99): ExecutorSourceInput | null => {
100 if (isInteractiveConnectPayload(payload)) {
101 return null;
102 }
103
104 switch (payload.kind) {
105 case "openapi":
106 return {
107 kind: "openapi",
108 endpoint: payload.endpoint,
109 specUrl: payload.specUrl,
110 name: payload.name,
111 namespace: payload.namespace,
112 importAuthPolicy: nullToUndefined(payload.importAuthPolicy),
113 importAuth: payload.importAuth,
114 auth: payload.auth,
115 };
116 case "graphql":
117 return {
118 kind: "graphql",
119 endpoint: payload.endpoint,
120 name: payload.name,
121 namespace: payload.namespace,
122 importAuthPolicy: nullToUndefined(payload.importAuthPolicy),
123 importAuth: payload.importAuth,
124 auth: payload.auth,
125 };
126 case "google_discovery":
127 return {
128 kind: "google_discovery",
129 service: payload.service,
130 version: payload.version,
131 discoveryUrl: payload.discoveryUrl,
132 scopes: payload.scopes,
133 workspaceOauthClientId: toWorkspaceOauthClientId(
134 payload.workspaceOauthClientId,
135 ),
136 oauthClient: payload.oauthClient,
137 name: payload.name,
138 namespace: payload.namespace,
139 importAuthPolicy: nullToUndefined(payload.importAuthPolicy),
140 importAuth: payload.importAuth,
141 auth: payload.auth,
142 };
143 }
144};
145
146const toExecutorMcpSourceInput = (
147 payload: Extract<ConnectSourcePayload, { kind?: "mcp" }>,
148 baseUrl: string | null,
149): ExecutorMcpSourceInput => ({
150 endpoint: payload.endpoint,
151 name: payload.name,
152 namespace: payload.namespace,
153 transport: payload.transport,
154 queryParams: payload.queryParams,
155 headers: payload.headers,
156 command: payload.command,
157 args: payload.args,
158 env: payload.env,
159 cwd: payload.cwd,
160 baseUrl,
161});
162
96const escapeHtml = (value: string): string =>163const escapeHtml = (value: string): string =>
97 value164 value
98 .replaceAll("&", "&amp;")165 .replaceAll("&", "&amp;")
@@ -595,8 +662,13 @@ const sourceOAuthPopupResultDocument = (input: { · 496 unmodified lines
595 </body>662 </body>
596</html>`;663</html>`;
597 664
598const htmlResponse = (html: string, status = 200) =>665const htmlResponse = (
599 HttpServerResponse.html(html).pipe(HttpServerResponse.setStatus(status));666 html: string,
667 status = 200,
668): ReturnType<typeof HttpServerResponse.html> =>
669 HttpServerResponse.html(html).pipe(
670 HttpServerResponse.setStatus(status),
671 ) as ReturnType<typeof HttpServerResponse.html>;
600 672
601const credentialErrorResponse = (input: {673const credentialErrorResponse = (input: {
602 title: string;674 title: string;
@@ -636,11 +708,16 @@ const credentialSubmitErrorResponse = (input: { · 33 unmodified lines
636 message: string;708 message: string;
637 status: number;709 status: number;
638}) =>710}) =>
639 getSourceCredentialInteraction({711 resolveRequestedLocalWorkspace(
640 workspaceId: input.workspaceId,712 "sources.credentialSubmit",
641 sourceId: input.sourceId,713 input.workspaceId,
642 interactionId: input.interactionId,714 ).pipe(
643 }).pipe(715 Effect.flatMap((executor) =>
716 executor.effect.local.credentials.get({
717 sourceId: input.sourceId,
718 interactionId: input.interactionId,
719 }),
720 ),
644 Effect.match({721 Effect.match({
645 onFailure: () =>722 onFailure: () =>
646 credentialErrorResponse({723 credentialErrorResponse({
@@ -671,53 +748,45 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 24 unmodified lines
671 (handlers) =>748 (handlers) =>
672 handlers749 handlers
673 .handle("discover", ({ payload }) =>750 .handle("discover", ({ payload }) =>
674 discoverSource({751 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
675 url: payload.url,752 executor.effect.sources.discover({
676 probeAuth: payload.probeAuth,753 url: payload.url,
677 }).pipe(754 probeAuth: payload.probeAuth,
678 Effect.catchAll((cause) =>755 }).pipe(
679 Effect.fail(toBadRequestError("sources.discover", cause)),756 Effect.catchAll((cause) =>
680 ),757 Effect.fail(toBadRequestError("sources.discover", cause)),
681 ),758 ),
759 )
760 )
682 )761 )
683 .handle("connect", ({ path, payload }) =>762 .handle("connect", ({ path, payload }) =>
684 resolveRequestedLocalWorkspace(763 resolveRequestedLocalWorkspace(
685 "sources.connect",764 "sources.connect",
686 path.workspaceId,765 path.workspaceId,
687 ).pipe(766 ).pipe(
688 Effect.flatMap((runtimeLocalWorkspace) =>767 Effect.flatMap((executor) =>
689 Effect.gen(function* () {768 Effect.gen(function* () {
690 const request = yield* HttpServerRequest.HttpServerRequest;769 const request = yield* HttpServerRequest.HttpServerRequest;
691 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
692 const baseUrl = resolveRequestOrigin(request);770 const baseUrl = resolveRequestOrigin(request);
693 771
694 if (isInteractiveConnectPayload(payload)) {772 if (isInteractiveConnectPayload(payload)) {
695 return yield* sourceAuthService.connectMcpSource({773 return yield* executor.effect.sources.connect(
696 workspaceId: path.workspaceId,774 toExecutorMcpSourceInput(payload, baseUrl),
697 actorAccountId: runtimeLocalWorkspace.installation.accountId,775 );
698 endpoint: payload.endpoint,
699 name: payload.name,
700 namespace: payload.namespace,
701 transport: payload.transport,
702 queryParams: payload.queryParams,
703 headers: payload.headers,
704 command: payload.command,
705 args: payload.args,
706 env: payload.env,
707 cwd: payload.cwd,
708 baseUrl,
709 });
710 }776 }
711 777
712 return yield* sourceAuthService.addExecutorSource(778 const sourceInput = toExecutorSourceInput(payload);
713 {779 if (sourceInput) {
714 workspaceId: path.workspaceId,780 return yield* executor.effect.sources.add(sourceInput, {
715 actorAccountId: runtimeLocalWorkspace.installation.accountId,781 baseUrl,
716 executionId: null,782 });
717 interactionId: null,783 }
718 ...(payload as Record<string, unknown>),784
719 } as ExecutorAddSourceInput,785 return yield* Effect.fail(
720 { baseUrl },786 toBadRequestError(
787 "sources.connect",
788 new Error("Unsupported source connect payload"),
789 ),
721 );790 );
722 }).pipe(791 }).pipe(
723 Effect.catchAll((cause) =>792 Effect.catchAll((cause) =>
@@ -732,20 +801,12 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
732 "sources.connectBatch",801 "sources.connectBatch",
733 path.workspaceId,802 path.workspaceId,
734 ).pipe(803 ).pipe(
735 Effect.flatMap((runtimeLocalWorkspace) =>804 Effect.flatMap((executor) =>
736 Effect.gen(function* () {805 Effect.gen(function* () {
737 const request = yield* HttpServerRequest.HttpServerRequest;806 const request = yield* HttpServerRequest.HttpServerRequest;
738 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;807 return yield* executor.effect.sources.connectBatch({
739 const baseUrl = resolveRequestOrigin(request);808 ...payload,
740 809 baseUrl: resolveRequestOrigin(request),
741 return yield* sourceAuthService.connectGoogleDiscoveryBatch({
742 workspaceId: path.workspaceId,
743 actorAccountId: runtimeLocalWorkspace.installation.accountId,
744 executionId: null,
745 interactionId: null,
746 workspaceOauthClientId: payload.workspaceOauthClientId,
747 sources: payload.sources,
748 baseUrl,
749 });810 });
750 }).pipe(811 }).pipe(
751 Effect.catchAll((cause) =>812 Effect.catchAll((cause) =>
@@ -760,20 +821,14 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
760 "sources.listWorkspaceOauthClients",821 "sources.listWorkspaceOauthClients",
761 path.workspaceId,822 path.workspaceId,
762 ).pipe(823 ).pipe(
763 Effect.flatMap(() =>824 Effect.flatMap((executor) =>
764 Effect.gen(function* () {825 executor.effect.sources.oauthClients.list(urlParams.providerKey).pipe(
765 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
766 return yield* sourceAuthService.listWorkspaceOauthClients({
767 workspaceId: path.workspaceId,
768 providerKey: urlParams.providerKey,
769 });
770 }).pipe(
771 Effect.catchAll((cause) =>826 Effect.catchAll((cause) =>
772 Effect.fail(827 Effect.fail(
773 toBadRequestError("sources.listWorkspaceOauthClients", cause),828 toBadRequestError("sources.listWorkspaceOauthClients", cause),
774 ),829 ),
775 ),830 ),
776 ),831 )
777 ),832 ),
778 ),833 ),
779 )834 )
@@ -782,16 +837,8 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
782 "sources.createWorkspaceOauthClient",837 "sources.createWorkspaceOauthClient",
783 path.workspaceId,838 path.workspaceId,
784 ).pipe(839 ).pipe(
785 Effect.flatMap(() =>840 Effect.flatMap((executor) =>
786 Effect.gen(function* () {841 executor.effect.sources.oauthClients.create(payload).pipe(
787 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
788 return yield* sourceAuthService.createWorkspaceOauthClient({
789 workspaceId: path.workspaceId,
790 providerKey: payload.providerKey,
791 label: payload.label,
792 oauthClient: payload.oauthClient,
793 });
794 }).pipe(
795 Effect.catchAll((cause) =>842 Effect.catchAll((cause) =>
796 Effect.fail(843 Effect.fail(
797 toBadRequestError(844 toBadRequestError(
@@ -800,7 +847,7 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
800 ),847 ),
801 ),848 ),
802 ),849 ),
803 ),850 )
804 ),851 ),
805 ),852 ),
806 )853 )
@@ -809,17 +856,9 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
809 "sources.removeWorkspaceOauthClient",856 "sources.removeWorkspaceOauthClient",
810 path.workspaceId,857 path.workspaceId,
811 ).pipe(858 ).pipe(
812 Effect.flatMap(() =>859 Effect.flatMap((executor) =>
813 Effect.gen(function* () {860 executor.effect.sources.oauthClients.remove(path.oauthClientId).pipe(
814 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;861 Effect.map((result) => ({ removed: result })),
815 const removed =
816 yield* sourceAuthService.removeWorkspaceOauthClient({
817 workspaceId: path.workspaceId,
818 oauthClientId: path.oauthClientId,
819 });
820
821 return { removed };
822 }).pipe(
823 Effect.catchAll((cause) =>862 Effect.catchAll((cause) =>
824 Effect.fail(863 Effect.fail(
825 toBadRequestError(864 toBadRequestError(
@@ -828,7 +867,7 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
828 ),867 ),
829 ),868 ),
830 ),869 ),
831 ),870 )
832 ),871 ),
833 ),872 ),
834 )873 )
@@ -837,60 +876,34 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
837 "sources.removeProviderAuthGrant",876 "sources.removeProviderAuthGrant",
838 path.workspaceId,877 path.workspaceId,
839 ).pipe(878 ).pipe(
840 Effect.flatMap(() =>879 Effect.flatMap((executor) =>
841 Effect.gen(function* () {880 executor.effect.sources.providerGrants.remove(path.grantId).pipe(
842 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;881 Effect.map((result) => ({ removed: result })),
843 const removed = yield* sourceAuthService882 Effect.catchAll((cause) =>
844 .removeProviderAuthGrant({883 Effect.fail(
845 workspaceId: path.workspaceId,884 toBadRequestError(
846 grantId: path.grantId,885 "sources.removeProviderAuthGrant",
847 })886 cause,
848 .pipe(
849 Effect.catchAll((cause) =>
850 Effect.fail(
851 toBadRequestError(
852 "sources.removeProviderAuthGrant",
853 cause,
854 ),
855 ),
856 ),887 ),
857 );888 ),
858 889 ),
859 return { removed };890 )
860 }),
861 ),891 ),
862 ),892 ),
863 )893 )
864 .handle("list", ({ path }) =>894 .handle("list", ({ path }) =>
865 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(895 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(
866 Effect.flatMap((runtimeLocalWorkspace) =>896 Effect.flatMap((executor) => executor.effect.sources.list()),
867 listSources({
868 workspaceId: path.workspaceId,
869 accountId: runtimeLocalWorkspace.installation.accountId,
870 }),
871 ),
872 ),897 ),
873 )898 )
874 .handle("create", ({ path, payload }) =>899 .handle("create", ({ path, payload }) =>
875 resolveRequestedLocalWorkspace("sources.create", path.workspaceId).pipe(900 resolveRequestedLocalWorkspace("sources.create", path.workspaceId).pipe(
876 Effect.flatMap((runtimeLocalWorkspace) =>901 Effect.flatMap((executor) => executor.effect.sources.create(payload)),
877 createSource({
878 workspaceId: path.workspaceId,
879 accountId: runtimeLocalWorkspace.installation.accountId,
880 payload,
881 }),
882 ),
883 ),902 ),
884 )903 )
885 .handle("get", ({ path }) =>904 .handle("get", ({ path }) =>
886 resolveRequestedLocalWorkspace("sources.get", path.workspaceId).pipe(905 resolveRequestedLocalWorkspace("sources.get", path.workspaceId).pipe(
887 Effect.flatMap((runtimeLocalWorkspace) =>906 Effect.flatMap((executor) => executor.effect.sources.get(path.sourceId)),
888 getSource({
889 workspaceId: path.workspaceId,
890 sourceId: path.sourceId,
891 accountId: runtimeLocalWorkspace.installation.accountId,
892 }),
893 ),
894 ),907 ),
895 )908 )
896 .handle("inspection", ({ path }) =>909 .handle("inspection", ({ path }) =>
@@ -898,11 +911,8 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 1 unmodified line
898 "sources.inspection",911 "sources.inspection",
899 path.workspaceId,912 path.workspaceId,
900 ).pipe(913 ).pipe(
901 Effect.zipRight(914 Effect.flatMap((executor) =>
902 getSourceInspection({915 executor.effect.sources.inspection.get(path.sourceId),
903 workspaceId: path.workspaceId,
904 sourceId: path.sourceId,
905 }),
906 ),916 ),
907 ),917 ),
908 )918 )
@@ -911,12 +921,11 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
911 "sources.inspection_tool",921 "sources.inspection_tool",
912 path.workspaceId,922 path.workspaceId,
913 ).pipe(923 ).pipe(
914 Effect.zipRight(924 Effect.flatMap((executor) =>
915 getSourceInspectionToolDetail({925 executor.effect.sources.inspection.tool({
916 workspaceId: path.workspaceId,926 sourceId: path.sourceId,
917 sourceId: path.sourceId,927 toolPath: path.toolPath,
918 toolPath: path.toolPath,928 }),
919 }),
920 ),929 ),
921 ),930 ),
922 )931 )
@@ -925,43 +934,38 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
925 "sources.inspection_discover",934 "sources.inspection_discover",
926 path.workspaceId,935 path.workspaceId,
927 ).pipe(936 ).pipe(
928 Effect.zipRight(937 Effect.flatMap((executor) =>
929 discoverSourceInspectionTools({938 executor.effect.sources.inspection.discover({
930 workspaceId: path.workspaceId,939 sourceId: path.sourceId,
931 sourceId: path.sourceId,940 payload,
932 payload,941 }),
933 }),
934 ),942 ),
935 ),943 ),
936 )944 )
937 .handle("update", ({ path, payload }) =>945 .handle("update", ({ path, payload }) =>
938 resolveRequestedLocalWorkspace("sources.update", path.workspaceId).pipe(946 resolveRequestedLocalWorkspace("sources.update", path.workspaceId).pipe(
939 Effect.flatMap((runtimeLocalWorkspace) =>947 Effect.flatMap((executor) =>
940 updateSource({948 executor.effect.sources.update(path.sourceId, payload)
941 workspaceId: path.workspaceId,
942 sourceId: path.sourceId,
943 accountId: runtimeLocalWorkspace.installation.accountId,
944 payload,
945 }),
946 ),949 ),
947 ),950 ),
948 )951 )
949 .handle("remove", ({ path }) =>952 .handle("remove", ({ path }) =>
950 resolveRequestedLocalWorkspace("sources.remove", path.workspaceId).pipe(953 resolveRequestedLocalWorkspace("sources.remove", path.workspaceId).pipe(
951 Effect.zipRight(954 Effect.flatMap((executor) => executor.effect.sources.remove(path.sourceId)),
952 removeSource({955 Effect.map((result) => ({ removed: result.removed })),
953 workspaceId: path.workspaceId,
954 sourceId: path.sourceId,
955 }),
956 ),
957 ),956 ),
958 )957 )
959 .handle("credentialPage", ({ path, urlParams }) =>958 .handle("credentialPage", ({ path, urlParams }) =>
960 getSourceCredentialInteraction({959 resolveRequestedLocalWorkspace(
961 workspaceId: path.workspaceId,960 "sources.credentialPage",
962 sourceId: path.sourceId,961 path.workspaceId,
963 interactionId: urlParams.interactionId,962 ).pipe(
964 }).pipe(963 Effect.flatMap((executor) =>
964 executor.effect.local.credentials.get({
965 sourceId: path.sourceId,
966 interactionId: urlParams.interactionId,
967 }),
968 ),
965 Effect.map((interaction) =>969 Effect.map((interaction) =>
966 htmlResponse(970 htmlResponse(
967 credentialPageDocument({971 credentialPageDocument({
@@ -976,39 +980,44 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
976 }),980 }),
977 ),981 ),
978 ),982 ),
979 Effect.catchTags({983 Effect.catchAll((error) =>
980 ControlPlaneNotFoundError: (error) =>984 Effect.succeed(
981 Effect.succeed(985 credentialErrorResponse({
982 credentialErrorResponse({986 title:
983 title: "Source Credential Request Unavailable",987 error instanceof ControlPlaneNotFoundError
984 message: error.message,988 ? "Source Credential Request Unavailable"
985 status: credentialErrorStatus(error),989 : "Source Credential Request Failed",
986 }),990 message:
987 ),991 error instanceof Error ? error.message : "Source credential request failed",
988 ControlPlaneStorageError: (error) =>992 status:
989 Effect.succeed(993 error instanceof ControlPlaneNotFoundError
990 credentialErrorResponse({994 || error instanceof ControlPlaneStorageError
991 title: "Source Credential Request Failed",995 || error instanceof ControlPlaneBadRequestError
992 message: error.message,996 ? credentialErrorStatus(error)
993 status: credentialErrorStatus(error),997 : 500,
994 }),998 }),
995 ),999 ),
996 }),1000 ),
997 ),1001 ),
998 )1002 )
999 .handle("credentialSubmit", ({ path, urlParams, payload }) =>1003 .handle("credentialSubmit", ({ path, urlParams, payload }) =>
1000 submitSourceCredentialInteraction({1004 resolveRequestedLocalWorkspace(
1001 workspaceId: path.workspaceId,1005 "sources.credentialSubmit",
1002 sourceId: path.sourceId,1006 path.workspaceId,
1003 interactionId: urlParams.interactionId,1007 ).pipe(
1004 action:1008 Effect.flatMap((executor) =>
1005 payload.action === "cancel"1009 executor.effect.local.credentials.submit({
1006 ? "cancel"1010 sourceId: path.sourceId,
1007 : payload.action === "continue"1011 interactionId: urlParams.interactionId,
1008 ? "continue"1012 action:
1009 : "submit",1013 payload.action === "cancel"
1010 token: payload.token,1014 ? "cancel"
1011 }).pipe(1015 : payload.action === "continue"
1016 ? "continue"
1017 : "submit",
1018 token: payload.token,
1019 }),
1020 ),
1012 Effect.map((result) =>1021 Effect.map((result) =>
1013 htmlResponse(1022 htmlResponse(
1014 credentialPageDocument({1023 credentialPageDocument({
@@ -1037,43 +1046,49 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 22 unmodified lines
1037 }),1046 }),
1038 ),1047 ),
1039 ),1048 ),
1040 Effect.catchTags({1049 Effect.catchAll((error) => {
1041 ControlPlaneBadRequestError: (error) =>1050 if (error instanceof ControlPlaneBadRequestError) {
1042 credentialSubmitErrorResponse({1051 return credentialSubmitErrorResponse({
1043 workspaceId: path.workspaceId,1052 workspaceId: path.workspaceId,
1044 sourceId: path.sourceId,1053 sourceId: path.sourceId,
1045 interactionId: urlParams.interactionId,1054 interactionId: urlParams.interactionId,
1046 message: error.message,1055 message: error.message,
1047 status: credentialErrorStatus(error),1056 status: credentialErrorStatus(error),
1057 });
1058 }
1059
1060 return Effect.succeed(
1061 credentialErrorResponse({
1062 title:
1063 error instanceof ControlPlaneNotFoundError
1064 ? "Source Credential Request Unavailable"
1065 : "Source Credential Request Failed",
1066 message:
1067 error instanceof Error ? error.message : "Source credential request failed",
1068 status:
1069 error instanceof ControlPlaneNotFoundError
1070 || error instanceof ControlPlaneStorageError
1071 ? credentialErrorStatus(error)
1072 : 500,
1048 }),1073 }),
1049 ControlPlaneNotFoundError: (error) =>1074 );
1050 Effect.succeed(
1051 credentialErrorResponse({
1052 title: "Source Credential Request Unavailable",
1053 message: error.message,
1054 status: credentialErrorStatus(error),
1055 }),
1056 ),
1057 ControlPlaneStorageError: (error) =>
1058 Effect.succeed(
1059 credentialErrorResponse({
1060 title: "Source Credential Request Failed",
1061 message: error.message,
1062 status: credentialErrorStatus(error),
1063 }),
1064 ),
1065 }),1075 }),
1066 ),1076 ),
1067 )1077 )
1068 .handle("credentialComplete", ({ path, urlParams }) =>1078 .handle("credentialComplete", ({ path, urlParams }) =>
1069 completeSourceCredentialSetup({1079 resolveRequestedLocalWorkspace(
1070 workspaceId: path.workspaceId,1080 "sources.credentialComplete",
1071 sourceId: path.sourceId,1081 path.workspaceId,
1072 state: urlParams.state,1082 ).pipe(
1073 code: urlParams.code,1083 Effect.flatMap((executor) =>
1074 error: urlParams.error,1084 executor.effect.local.credentials.complete({
1075 errorDescription: urlParams.error_description,1085 sourceId: path.sourceId,
1076 }).pipe(1086 state: urlParams.state,
1087 code: urlParams.code,
1088 error: urlParams.error,
1089 errorDescription: urlParams.error_description,
1090 }),
1091 ),
1077 Effect.map((completed) =>1092 Effect.map((completed) =>
1078 htmlResponse(1093 htmlResponse(
1079 sourceOAuthPopupResultDocument({1094 sourceOAuthPopupResultDocument({
@@ -1119,13 +1134,10 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 39 unmodified lines
1119 "sources.providerOauthComplete",1134 "sources.providerOauthComplete",
1120 path.workspaceId,1135 path.workspaceId,
1121 ).pipe(1136 ).pipe(
1122 Effect.flatMap((runtimeLocalWorkspace) =>1137 Effect.flatMap((executor) =>
1123 Effect.gen(function* () {1138 Effect.gen(function* () {
1124 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;1139 const completed = yield* executor.effect.oauth.completeProviderCallback({
1125 const completed =
1126 yield* sourceAuthService.completeProviderOauthCallback({
1127 workspaceId: path.workspaceId,1140 workspaceId: path.workspaceId,
1128 actorAccountId: runtimeLocalWorkspace.installation.accountId,
1129 state: urlParams.state,1141 state: urlParams.state,
1130 code: urlParams.code,1142 code: urlParams.code,
1131 error: urlParams.error,1143 error: urlParams.error,
packages/platform/internal/src/tools/executor-admin.ts+309−26
@@ -1,4 +1,5 @@
1import { toTool, type ToolMap } from "@executor/codemode-core";1import { toTool, type ToolMap } from "@executor/codemode-core";
2import type { Executor } from "@executor/platform-sdk";
2import {3import {
3 type AccountId,4 type AccountId,
4 LocalInstallationSchema,5 LocalInstallationSchema,
@@ -45,47 +46,39 @@ import { · 40 unmodified lines
45 UpdateSecretPayloadSchema,46 UpdateSecretPayloadSchema,
46 UpdateSecretResultSchema,47 UpdateSecretResultSchema,
47 type UpdateSecretPayload,48 type UpdateSecretPayload,
48} from "@executor/platform-sdk/local/contracts";49} from "@executor/platform-sdk/contracts";
49import {50import {
50 createLocalSecret,51 createLocalSecret,
52 createPolicy,
51 deleteLocalSecret,53 deleteLocalSecret,
54 discoverSource,
55 discoverSourceInspectionTools,
52 getLocalInstanceConfig,56 getLocalInstanceConfig,
57 getPolicy,
58 getSource,
59 getSourceInspection,
60 getSourceInspectionToolDetail,
53 listLocalSecrets,61 listLocalSecrets,
62 listPolicies,
63 listSources,
64 removePolicy,
65 removeSource,
54 updateLocalSecret,66 updateLocalSecret,
55} from "@executor/platform-sdk/local/secrets";67 updatePolicy,
68 updateSource,
69} from "@executor/platform-sdk/operations";
56import {70import {
57 CreatePolicyPayloadSchema,71 CreatePolicyPayloadSchema,
58 type CreatePolicyPayload,72 type CreatePolicyPayload,
59 type UpdatePolicyPayload,73 type UpdatePolicyPayload,
60 UpdatePolicyPayloadSchema,74 UpdatePolicyPayloadSchema,
61} from "@executor/platform-sdk/policies/contracts";75} from "@executor/platform-sdk/contracts";
62import {
63 createPolicy,
64 getPolicy,
65 listPolicies,
66 removePolicy,
67 updatePolicy,
68} from "@executor/platform-sdk/policies/operations";
69import {76import {
70 CreateWorkspaceOauthClientPayloadSchema,77 CreateWorkspaceOauthClientPayloadSchema,
71 DiscoverSourcePayloadSchema,78 DiscoverSourcePayloadSchema,
72 type CreateWorkspaceOauthClientPayload,79 type CreateWorkspaceOauthClientPayload,
73 UpdateSourcePayloadSchema,80 UpdateSourcePayloadSchema,
74} from "@executor/platform-sdk/sources/contracts";81} from "@executor/platform-sdk/contracts";
75import {
76 discoverSource,
77} from "@executor/platform-sdk/sources/discovery";
78import {
79 discoverSourceInspectionTools,
80 getSourceInspection,
81 getSourceInspectionToolDetail,
82} from "@executor/platform-sdk/sources/inspection";
83import {
84 getSource,
85 listSources,
86 removeSource,
87 updateSource,
88} from "@executor/platform-sdk/sources/operations";
89 82
90const emptyInputSchema = Schema.standardSchemaV1(Schema.Struct({}));83const emptyInputSchema = Schema.standardSchemaV1(Schema.Struct({}));
91const localInstallationOutputSchema = Schema.standardSchemaV1(84const localInstallationOutputSchema = Schema.standardSchemaV1(
@@ -260,7 +253,7 @@ const runWorkspaceStorageEffect = <A, E, R>(input: { · 168 unmodified lines
260 ) as Effect.Effect<A, E, never>,253 ) as Effect.Effect<A, E, never>,
261 );254 );
262 255
263export const createExecutorAdminToolMap = (256export const createWorkspaceExecutorAdminToolMap = (
264 input: WorkspaceInternalToolContext,257 input: WorkspaceInternalToolContext,
265): ToolMap => {258): ToolMap => {
266 const runtimeLayer = makeRuntimeLayer({259 const runtimeLayer = makeRuntimeLayer({
@@ -690,3 +683,293 @@ export const createExecutorAdminToolMap = ( · 423 unmodified lines
690 }),683 }),
691 };684 };
692};685};
686
687export const createExecutorAdminToolMap = (input: {
688 executor: Executor;
689}): ToolMap => {
690 const metadata = {
691 sourceKey: "executor",
692 interaction: "auto" as const,
693 };
694
695 return {
696 "executor.local.installation.get": toTool({
697 tool: {
698 description:
699 "Get the active local executor installation account and workspace ids.",
700 inputSchema: emptyInputSchema,
701 outputSchema: localInstallationOutputSchema,
702 execute: () => Promise.resolve(input.executor.installation),
703 },
704 metadata,
705 }),
706 "executor.local.config.get": toTool({
707 tool: {
708 description:
709 "Get local instance config such as supported secret providers.",
710 inputSchema: emptyInputSchema,
711 outputSchema: instanceConfigOutputSchema,
712 execute: () => input.executor.local.config(),
713 },
714 metadata,
715 }),
716 "executor.secrets.list": toTool({
717 tool: {
718 description:
719 "List locally stored secrets and the sources linked to them.",
720 inputSchema: emptyInputSchema,
721 outputSchema: secretListOutputSchema,
722 execute: () => input.executor.secrets.list(),
723 },
724 metadata,
725 }),
726 "executor.secrets.create": toTool({
727 tool: {
728 description:
729 "Create a local secret without putting the raw value into source config.",
730 inputSchema: createSecretInputSchema,
731 outputSchema: Schema.standardSchemaV1(CreateSecretResultSchema),
732 execute: (payload: CreateSecretPayload) =>
733 input.executor.secrets.create(payload),
734 },
735 metadata,
736 }),
737 "executor.secrets.update": toTool({
738 tool: {
739 description:
740 "Update a stored secret name and optionally rotate its value.",
741 inputSchema: updateSecretInputSchema,
742 outputSchema: Schema.standardSchemaV1(UpdateSecretResultSchema),
743 execute: (payload: {
744 secretId: string;
745 payload: UpdateSecretPayload;
746 }) => input.executor.secrets.update(payload),
747 },
748 metadata,
749 }),
750 "executor.secrets.remove": toTool({
751 tool: {
752 description: "Remove a stored local secret.",
753 inputSchema: removeSecretInputSchema,
754 outputSchema: Schema.standardSchemaV1(DeleteSecretResultSchema),
755 execute: ({ secretId }: { secretId: string }) =>
756 input.executor.secrets.remove(secretId),
757 },
758 metadata,
759 }),
760 "executor.sources.discover": toTool({
761 tool: {
762 description:
763 "Probe a URL and infer whether it looks like MCP, OpenAPI, GraphQL, or another supported source.",
764 inputSchema: discoverSourceInputSchema,
765 outputSchema: sourceDiscoveryOutputSchema,
766 execute: (payload: { url: string; probeAuth?: unknown }) =>
767 input.executor.sources.discover({
768 url: payload.url,
769 probeAuth: payload.probeAuth as never,
770 }),
771 },
772 metadata,
773 }),
774 "executor.sources.list": toTool({
775 tool: {
776 description: "List sources connected in the current workspace.",
777 inputSchema: emptyInputSchema,
778 outputSchema: listSourcesOutputSchema,
779 execute: () => input.executor.sources.list(),
780 },
781 metadata,
782 }),
783 "executor.sources.get": toTool({
784 tool: {
785 description: "Get one source by id.",
786 inputSchema: getSourceInputSchema,
787 outputSchema: sourceOutputSchema,
788 execute: ({ sourceId }: { sourceId: string }) =>
789 input.executor.sources.get(sourceId as never),
790 },
791 metadata,
792 }),
793 "executor.sources.update": toTool({
794 tool: {
795 description: "Update a source definition in the current workspace.",
796 inputSchema: updateSourceInputSchema,
797 outputSchema: sourceOutputSchema,
798 execute: (payload: {
799 sourceId: string;
800 payload: Record<string, unknown>;
801 }) =>
802 input.executor.sources.update(
803 payload.sourceId as never,
804 payload.payload as never,
805 ),
806 },
807 metadata,
808 }),
809 "executor.sources.remove": toTool({
810 tool: {
811 description: "Remove a source from the current workspace.",
812 inputSchema: getSourceInputSchema,
813 outputSchema: removeResultSchema,
814 execute: async ({ sourceId }: { sourceId: string }) => ({
815 removed: await input.executor.sources.remove(sourceId as never),
816 }),
817 },
818 metadata,
819 }),
820 "executor.sources.inspect.get": toTool({
821 tool: {
822 description: "Inspect the tool model for one connected source.",
823 inputSchema: getSourceInputSchema,
824 outputSchema: sourceInspectionOutputSchema,
825 execute: ({ sourceId }: { sourceId: string }) =>
826 input.executor.sources.inspection.get(sourceId as never),
827 },
828 metadata,
829 }),
830 "executor.sources.inspect.tool": toTool({
831 tool: {
832 description: "Inspect one tool inside a connected source.",
833 inputSchema: inspectToolInputSchema,
834 outputSchema: sourceInspectionToolOutputSchema,
835 execute: ({
836 sourceId,
837 toolPath,
838 }: {
839 sourceId: string;
840 toolPath: string;
841 }) =>
842 input.executor.sources.inspection.tool({
843 sourceId: sourceId as never,
844 toolPath,
845 }),
846 },
847 metadata,
848 }),
849 "executor.sources.inspect.discover": toTool({
850 tool: {
851 description: "Search within a single source's inspected tools.",
852 inputSchema: inspectDiscoverInputSchema,
853 outputSchema: sourceInspectionDiscoverOutputSchema,
854 execute: ({
855 sourceId,
856 payload,
857 }: {
858 sourceId: string;
859 payload: { query: string; limit?: number };
860 }) =>
861 input.executor.sources.inspection.discover({
862 sourceId: sourceId as never,
863 payload: payload as never,
864 }),
865 },
866 metadata,
867 }),
868 "executor.sources.oauthClients.list": toTool({
869 tool: {
870 description: "List stored workspace OAuth clients for one provider.",
871 inputSchema: workspaceOauthClientListInputSchema,
872 outputSchema: workspaceOauthClientListOutputSchema,
873 execute: ({ providerKey }: { providerKey: string }) =>
874 input.executor.sources.oauthClients.list(providerKey),
875 },
876 metadata,
877 }),
878 "executor.sources.oauthClients.create": toTool({
879 tool: {
880 description:
881 "Create a workspace OAuth client for a provider-backed source flow.",
882 inputSchema: createWorkspaceOauthClientInputSchema,
883 outputSchema: Schema.standardSchemaV1(WorkspaceOauthClientSchema),
884 execute: (payload: CreateWorkspaceOauthClientPayload) =>
885 input.executor.sources.oauthClients.create(payload),
886 },
887 metadata,
888 }),
889 "executor.sources.oauthClients.remove": toTool({
890 tool: {
891 description: "Remove a stored workspace OAuth client.",
892 inputSchema: removeWorkspaceOauthClientInputSchema,
893 outputSchema: removeResultSchema,
894 execute: async ({
895 oauthClientId,
896 }: {
897 oauthClientId: string;
898 }) => ({
899 removed: await input.executor.sources.oauthClients.remove(
900 oauthClientId as never,
901 ),
902 }),
903 },
904 metadata,
905 }),
906 "executor.sources.providerGrants.remove": toTool({
907 tool: {
908 description: "Remove one stored provider auth grant.",
909 inputSchema: removeProviderGrantInputSchema,
910 outputSchema: removeResultSchema,
911 execute: async ({ grantId }: { grantId: string }) => ({
912 removed: await input.executor.sources.providerGrants.remove(
913 grantId as never,
914 ),
915 }),
916 },
917 metadata,
918 }),
919 "executor.policies.list": toTool({
920 tool: {
921 description: "List local workspace policies.",
922 inputSchema: emptyInputSchema,
923 outputSchema: listPoliciesOutputSchema,
924 execute: () => input.executor.policies.list(),
925 },
926 metadata,
927 }),
928 "executor.policies.create": toTool({
929 tool: {
930 description: "Create a local workspace policy.",
931 inputSchema: createPolicyInputSchema,
932 outputSchema: localWorkspacePolicyOutputSchema,
933 execute: (payload: CreatePolicyPayload) =>
934 input.executor.policies.create(payload),
935 },
936 metadata,
937 }),
938 "executor.policies.get": toTool({
939 tool: {
940 description: "Get one local workspace policy by id.",
941 inputSchema: policyIdInputSchema,
942 outputSchema: localWorkspacePolicyOutputSchema,
943 execute: ({ policyId }: { policyId: string }) =>
944 input.executor.policies.get(policyId),
945 },
946 metadata,
947 }),
948 "executor.policies.update": toTool({
949 tool: {
950 description: "Update a local workspace policy.",
951 inputSchema: updatePolicyInputSchema,
952 outputSchema: localWorkspacePolicyOutputSchema,
953 execute: ({
954 policyId,
955 payload,
956 }: {
957 policyId: string;
958 payload: UpdatePolicyPayload;
959 }) => input.executor.policies.update(policyId, payload),
960 },
961 metadata,
962 }),
963 "executor.policies.remove": toTool({
964 tool: {
965 description: "Remove a local workspace policy.",
966 inputSchema: policyIdInputSchema,
967 outputSchema: removeResultSchema,
968 execute: async ({ policyId }: { policyId: string }) => ({
969 removed: await input.executor.policies.remove(policyId),
970 }),
971 },
972 metadata,
973 }),
974 };
975};
packages/platform/sdk/package.json+5−11
@@ -4,20 +4,14 @@ · 3 unmodified lines
4 "type": "module",4 "type": "module",
5 "exports": {5 "exports": {
6 ".": "./src/index.ts",6 ".": "./src/index.ts",
7 "./backend": "./src/backend.ts",
8 "./contracts": "./src/contracts.ts",
9 "./executor": "./src/executor.ts",
7 "./errors": "./src/errors.ts",10 "./errors": "./src/errors.ts",
11 "./operations": "./src/operations.ts",
8 "./string-schemas": "./src/string-schemas.ts",12 "./string-schemas": "./src/string-schemas.ts",
9 "./executions/contracts": "./src/executions/contracts.ts",
10 "./schema": "./src/schema/index.ts",13 "./schema": "./src/schema/index.ts",
11 "./runtime": "./src/runtime/index.ts",14 "./runtime": "./src/runtime/index.ts"
12 "./local/contracts": "./src/local/contracts.ts",
13 "./local/operations": "./src/local/operations.ts",
14 "./local/secrets": "./src/local/secrets.ts",
15 "./policies/contracts": "./src/policies/contracts.ts",
16 "./policies/operations": "./src/policies/operations.ts",
17 "./sources/contracts": "./src/sources/contracts.ts",
18 "./sources/discovery": "./src/sources/discovery.ts",
19 "./sources/inspection": "./src/sources/inspection.ts",
20 "./sources/operations": "./src/sources/operations.ts"
21 },15 },
22 "imports": {16 "imports": {
23 "#runtime": "./src/runtime/index.ts",17 "#runtime": "./src/runtime/index.ts",
packages/platform/sdk/src/backend.ts+42−0
@@ -0,0 +1,42 @@
1import * as Effect from "effect/Effect";
2
3import {
4 createControlPlaneRuntimeFromServices,
5 type BoundInstallationStore,
6 type BoundLocalToolRuntimeLoader,
7 type BoundSourceArtifactStore,
8 type BoundWorkspaceConfigStore,
9 type BoundWorkspaceStateStore,
10 type ControlPlaneRuntime,
11 type RuntimeControlPlaneOptions,
12 type RuntimeControlPlaneServices,
13 type RuntimeSecretMaterialServices,
14} from "./runtime";
15
16export type ExecutorBackend = {
17 createRuntime: (
18 options: RuntimeControlPlaneOptions,
19 ) => Effect.Effect<ControlPlaneRuntime, Error>;
20};
21
22export type ExecutorBackendServices = RuntimeControlPlaneServices;
23export type ExecutorInstallationBackend = BoundInstallationStore;
24export type ExecutorWorkspaceConfigBackend = BoundWorkspaceConfigStore;
25export type ExecutorWorkspaceStateBackend = BoundWorkspaceStateStore;
26export type ExecutorSourceArtifactBackend = BoundSourceArtifactStore;
27export type ExecutorLocalToolBackend = BoundLocalToolRuntimeLoader;
28export type ExecutorSecretMaterialBackend = RuntimeSecretMaterialServices;
29
30export const createExecutorBackend = (input: {
31 loadServices: (
32 options: RuntimeControlPlaneOptions,
33 ) => Effect.Effect<ExecutorBackendServices, Error>;
34}): ExecutorBackend => ({
35 createRuntime: (options) =>
36 Effect.flatMap(input.loadServices(options), (services) =>
37 createControlPlaneRuntimeFromServices({
38 ...options,
39 services,
40 }),
41 ),
42});
packages/platform/sdk/src/contracts.ts+4−0
@@ -0,0 +1,4 @@
1export * from "./executions/contracts";
2export * from "./local/contracts";
3export * from "./policies/contracts";
4export * from "./sources/contracts";
packages/platform/sdk/src/executor.ts+694−0
@@ -0,0 +1,694 @@
1import * as Effect from "effect/Effect";
2
3import type {
4 AccountId,
5 Execution,
6 ExecutionEnvelope,
7 ExecutionInteraction,
8 LocalInstallation,
9 LocalWorkspacePolicy,
10 ProviderAuthGrant,
11 Source,
12 WorkspaceId,
13 WorkspaceOauthClient,
14} from "./schema";
15import { ExecutionIdSchema } from "./schema";
16import type { CreateExecutionPayload, ResumeExecutionPayload } from "./executions/contracts";
17import type {
18 CreateSecretPayload,
19 CreateSecretResult,
20 DeleteSecretResult,
21 InstanceConfig,
22 SecretListItem,
23 UpdateSecretPayload,
24 UpdateSecretResult,
25} from "./local/contracts";
26import {
27 completeSourceCredentialSetup,
28 getLocalInstallation,
29 getSourceCredentialInteraction,
30 submitSourceCredentialInteraction,
31} from "./local/operations";
32import {
33 createLocalSecret,
34 deleteLocalSecret,
35 getLocalInstanceConfig,
36 listLocalSecrets,
37 updateLocalSecret,
38} from "./local/secrets";
39import type { CreatePolicyPayload, UpdatePolicyPayload } from "./policies/contracts";
40import {
41 createPolicy,
42 getPolicy,
43 listPolicies,
44 removePolicy,
45 updatePolicy,
46} from "./policies/operations";
47import type {
48 CreateSourcePayload,
49 CreateWorkspaceOauthClientPayload,
50 UpdateSourcePayload,
51} from "./sources/contracts";
52import { discoverSource } from "./sources/discovery";
53import {
54 discoverSourceInspectionTools,
55 getSourceInspection,
56 getSourceInspectionToolDetail,
57} from "./sources/inspection";
58import {
59 createSource,
60 getSource,
61 listSources,
62 removeSource,
63 updateSource,
64} from "./sources/operations";
65import type { ExecutorBackend } from "./backend";
66import {
67 provideControlPlaneRuntime,
68 type ControlPlaneRuntime,
69 type CreateWorkspaceInternalToolMap,
70 type ResolveExecutionEnvironment,
71 type ResolveSecretMaterial,
72 RuntimeSourceAuthServiceTag,
73} from "./runtime";
74import { createExecution, getExecution, resumeExecution } from "./runtime/execution/service";
75import type {
76 CompleteProviderOauthCallbackResult,
77 CompleteSourceCredentialSetupResult,
78 CompleteSourceOAuthSessionResult,
79 ConnectGoogleDiscoveryBatchInput,
80 ConnectGoogleDiscoveryBatchResult,
81 ConnectMcpSourceInput,
82 ExecutorAddSourceInput,
83 ExecutorSourceAddResult,
84 McpSourceConnectResult,
85 StartSourceOAuthSessionInput,
86 StartSourceOAuthSessionResult,
87} from "./runtime/sources/source-auth-service";
88
89export type ExecutorRuntimeOptions = {
90 executionResolver?: ResolveExecutionEnvironment;
91 createInternalToolMap?: CreateWorkspaceInternalToolMap;
92 resolveSecretMaterial?: ResolveSecretMaterial;
93 getLocalServerBaseUrl?: () => string | undefined;
94};
95
96type DistributiveOmit<T, Keys extends PropertyKey> = T extends unknown ? Omit<T, Keys> : never;
97type ProvidedEffect<T extends Effect.Effect<any, any, any>> = Effect.Effect<
98 Effect.Effect.Success<T>,
99 Effect.Effect.Error<T>,
100 never
101>;
102
103export type ExecutorSourceInput = DistributiveOmit<
104 ExecutorAddSourceInput,
105 "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
106>;
107
108export type ExecutorSourceBatchInput = DistributiveOmit<
109 ConnectGoogleDiscoveryBatchInput,
110 "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
111>;
112
113export type ExecutorMcpSourceInput = DistributiveOmit<
114 ConnectMcpSourceInput,
115 "workspaceId" | "actorAccountId"
116>;
117
118export type ExecutorSourceOAuthInput = DistributiveOmit<
119 StartSourceOAuthSessionInput,
120 "workspaceId" | "actorAccountId"
121>;
122
123export type Executor = {
124 runtime: ControlPlaneRuntime;
125 installation: LocalInstallation;
126 workspaceId: WorkspaceId;
127 accountId: AccountId;
128 provide: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, any>;
129 run: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
130 close: () => Promise<void>;
131 effect: {
132 local: {
133 installation: () => ProvidedEffect<ReturnType<typeof getLocalInstallation>>;
134 config: () => ProvidedEffect<ReturnType<typeof getLocalInstanceConfig>>;
135 credentials: {
136 get: (input: {
137 sourceId: Source["id"];
138 interactionId: ExecutionInteraction["id"];
139 }) => ProvidedEffect<ReturnType<typeof getSourceCredentialInteraction>>;
140 submit: (input: {
141 sourceId: Source["id"];
142 interactionId: ExecutionInteraction["id"];
143 action: "submit" | "continue" | "cancel";
144 token?: string | null;
145 }) => ProvidedEffect<ReturnType<typeof submitSourceCredentialInteraction>>;
146 complete: (input: {
147 sourceId: Source["id"];
148 state: string;
149 code?: string | null;
150 error?: string | null;
151 errorDescription?: string | null;
152 }) => ProvidedEffect<ReturnType<typeof completeSourceCredentialSetup>>;
153 };
154 };
155 secrets: {
156 list: () => ProvidedEffect<ReturnType<typeof listLocalSecrets>>;
157 create: (payload: CreateSecretPayload) => ProvidedEffect<ReturnType<typeof createLocalSecret>>;
158 update: (input: {
159 secretId: string;
160 payload: UpdateSecretPayload;
161 }) => ProvidedEffect<ReturnType<typeof updateLocalSecret>>;
162 remove: (secretId: string) => ProvidedEffect<ReturnType<typeof deleteLocalSecret>>;
163 };
164 policies: {
165 list: () => ProvidedEffect<ReturnType<typeof listPolicies>>;
166 create: (payload: CreatePolicyPayload) => ProvidedEffect<ReturnType<typeof createPolicy>>;
167 get: (policyId: string) => ProvidedEffect<ReturnType<typeof getPolicy>>;
168 update: (
169 policyId: string,
170 payload: UpdatePolicyPayload,
171 ) => ProvidedEffect<ReturnType<typeof updatePolicy>>;
172 remove: (policyId: string) => ProvidedEffect<ReturnType<typeof removePolicy>>;
173 };
174 sources: {
175 add: (
176 input: ExecutorSourceInput,
177 options?: {
178 baseUrl?: string | null;
179 },
180 ) => Effect.Effect<ExecutorSourceAddResult, Error, never>;
181 connect: (payload: ExecutorMcpSourceInput) => Effect.Effect<McpSourceConnectResult, Error, never>;
182 connectBatch: (
183 payload: ExecutorSourceBatchInput,
184 ) => Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, never>;
185 discover: (input: {
186 url: string;
187 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
188 }) => ProvidedEffect<ReturnType<typeof discoverSource>>;
189 list: () => ProvidedEffect<ReturnType<typeof listSources>>;
190 create: (payload: CreateSourcePayload) => ProvidedEffect<ReturnType<typeof createSource>>;
191 get: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof getSource>>;
192 update: (
193 sourceId: Source["id"],
194 payload: UpdateSourcePayload,
195 ) => ProvidedEffect<ReturnType<typeof updateSource>>;
196 remove: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof removeSource>>;
197 inspection: {
198 get: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof getSourceInspection>>;
199 tool: (input: {
200 sourceId: Source["id"];
201 toolPath: string;
202 }) => ProvidedEffect<ReturnType<typeof getSourceInspectionToolDetail>>;
203 discover: (input: {
204 sourceId: Source["id"];
205 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
206 }) => ProvidedEffect<ReturnType<typeof discoverSourceInspectionTools>>;
207 };
208 oauthClients: {
209 list: (
210 providerKey: string,
211 ) => Effect.Effect<ReadonlyArray<WorkspaceOauthClient>, Error, never>;
212 create: (
213 payload: CreateWorkspaceOauthClientPayload,
214 ) => Effect.Effect<WorkspaceOauthClient, Error, never>;
215 remove: (
216 oauthClientId: WorkspaceOauthClient["id"],
217 ) => Effect.Effect<boolean, Error, never>;
218 };
219 providerGrants: {
220 remove: (grantId: ProviderAuthGrant["id"]) => Effect.Effect<boolean, Error, never>;
221 };
222 };
223 oauth: {
224 startSourceAuth: (
225 input: ExecutorSourceOAuthInput,
226 ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
227 completeSourceAuth: (input: {
228 state: string;
229 code?: string | null;
230 error?: string | null;
231 errorDescription?: string | null;
232 }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>;
233 completeProviderCallback: (input: {
234 workspaceId?: WorkspaceId;
235 actorAccountId?: AccountId | null;
236 state: string;
237 code?: string | null;
238 error?: string | null;
239 errorDescription?: string | null;
240 }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, never>;
241 };
242 executions: {
243 create: (payload: CreateExecutionPayload) => ProvidedEffect<ReturnType<typeof createExecution>>;
244 get: (executionId: Execution["id"]) => ProvidedEffect<ReturnType<typeof getExecution>>;
245 resume: (
246 executionId: Execution["id"],
247 payload: ResumeExecutionPayload,
248 ) => ProvidedEffect<ReturnType<typeof resumeExecution>>;
249 };
250 };
251 local: {
252 installation: () => Promise<LocalInstallation>;
253 config: () => Promise<InstanceConfig>;
254 credentials: {
255 get: (input: {
256 sourceId: Source["id"];
257 interactionId: ExecutionInteraction["id"];
258 }) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceCredentialInteraction>>>;
259 submit: (input: {
260 sourceId: Source["id"];
261 interactionId: ExecutionInteraction["id"];
262 action: "submit" | "continue" | "cancel";
263 token?: string | null;
264 }) => Promise<Effect.Effect.Success<ReturnType<typeof submitSourceCredentialInteraction>>>;
265 complete: (input: {
266 sourceId: Source["id"];
267 state: string;
268 code?: string | null;
269 error?: string | null;
270 errorDescription?: string | null;
271 }) => Promise<CompleteSourceCredentialSetupResult>;
272 };
273 };
274 secrets: {
275 list: () => Promise<ReadonlyArray<SecretListItem>>;
276 create: (payload: CreateSecretPayload) => Promise<CreateSecretResult>;
277 update: (input: {
278 secretId: string;
279 payload: UpdateSecretPayload;
280 }) => Promise<UpdateSecretResult>;
281 remove: (secretId: string) => Promise<DeleteSecretResult>;
282 };
283 policies: {
284 list: () => Promise<ReadonlyArray<LocalWorkspacePolicy>>;
285 create: (payload: CreatePolicyPayload) => Promise<LocalWorkspacePolicy>;
286 get: (policyId: string) => Promise<LocalWorkspacePolicy>;
287 update: (
288 policyId: string,
289 payload: UpdatePolicyPayload,
290 ) => Promise<LocalWorkspacePolicy>;
291 remove: (policyId: string) => Promise<boolean>;
292 };
293 sources: {
294 add: (
295 input: ExecutorSourceInput,
296 options?: {
297 baseUrl?: string | null;
298 },
299 ) => Promise<ExecutorSourceAddResult>;
300 connect: (payload: ExecutorMcpSourceInput) => Promise<McpSourceConnectResult>;
301 connectBatch: (payload: ExecutorSourceBatchInput) => Promise<ConnectGoogleDiscoveryBatchResult>;
302 discover: (input: {
303 url: string;
304 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
305 }) => Promise<Effect.Effect.Success<ReturnType<typeof discoverSource>>>;
306 list: () => Promise<ReadonlyArray<Source>>;
307 create: (payload: CreateSourcePayload) => Promise<Source>;
308 get: (sourceId: Source["id"]) => Promise<Source>;
309 update: (sourceId: Source["id"], payload: UpdateSourcePayload) => Promise<Source>;
310 remove: (sourceId: Source["id"]) => Promise<boolean>;
311 inspection: {
312 get: (sourceId: Source["id"]) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceInspection>>>;
313 tool: (input: {
314 sourceId: Source["id"];
315 toolPath: string;
316 }) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceInspectionToolDetail>>>;
317 discover: (input: {
318 sourceId: Source["id"];
319 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
320 }) => Promise<Effect.Effect.Success<ReturnType<typeof discoverSourceInspectionTools>>>;
321 };
322 oauthClients: {
323 list: (providerKey: string) => Promise<ReadonlyArray<WorkspaceOauthClient>>;
324 create: (
325 payload: CreateWorkspaceOauthClientPayload,
326 ) => Promise<WorkspaceOauthClient>;
327 remove: (oauthClientId: WorkspaceOauthClient["id"]) => Promise<boolean>;
328 };
329 providerGrants: {
330 remove: (grantId: ProviderAuthGrant["id"]) => Promise<boolean>;
331 };
332 };
333 oauth: {
334 startSourceAuth: (input: ExecutorSourceOAuthInput) => Promise<StartSourceOAuthSessionResult>;
335 completeSourceAuth: (input: {
336 state: string;
337 code?: string | null;
338 error?: string | null;
339 errorDescription?: string | null;
340 }) => Promise<CompleteSourceOAuthSessionResult>;
341 completeProviderCallback: (input: {
342 workspaceId?: WorkspaceId;
343 actorAccountId?: AccountId | null;
344 state: string;
345 code?: string | null;
346 error?: string | null;
347 errorDescription?: string | null;
348 }) => Promise<CompleteProviderOauthCallbackResult>;
349 };
350 executions: {
351 create: (payload: CreateExecutionPayload) => Promise<ExecutionEnvelope>;
352 get: (executionId: Execution["id"]) => Promise<ExecutionEnvelope>;
353 resume: (
354 executionId: Execution["id"],
355 payload: ResumeExecutionPayload,
356 ) => Promise<ExecutionEnvelope>;
357 };
358};
359
360export type CreateExecutorOptions = ExecutorRuntimeOptions & {
361 backend: ExecutorBackend;
362};
363
364const fromRuntime = (runtime: ControlPlaneRuntime): Executor => {
365 const installation = runtime.localInstallation;
366 const workspaceId = installation.workspaceId;
367 const accountId = installation.accountId;
368 const provide = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
369 provideControlPlaneRuntime(effect, runtime);
370 const run = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
371 Effect.runPromise(provide(effect) as Effect.Effect<A, E, never>);
372 const provideSourceAuth = <A, E>(
373 execute: (
374 service: Effect.Effect.Success<typeof RuntimeSourceAuthServiceTag>,
375 ) => Effect.Effect<A, E, any>,
376 ) => provide(Effect.flatMap(RuntimeSourceAuthServiceTag, execute));
377 const createSdkSourceSession = () => {
378 const id = crypto.randomUUID();
379 return {
380 executionId: ExecutionIdSchema.make(`exec_sdk_${id}`),
381 interactionId: `executor.sdk.${id}` as never,
382 };
383 };
384 const effect = {
385 local: {
386 installation: () => provide(getLocalInstallation()),
387 config: () => provide(getLocalInstanceConfig()),
388 credentials: {
389 get: ({ sourceId, interactionId }) =>
390 provide(
391 getSourceCredentialInteraction({
392 workspaceId,
393 sourceId,
394 interactionId,
395 }),
396 ),
397 submit: ({ sourceId, interactionId, action, token }) =>
398 provide(
399 submitSourceCredentialInteraction({
400 workspaceId,
401 sourceId,
402 interactionId,
403 action,
404 token,
405 }),
406 ),
407 complete: ({ sourceId, state, code, error, errorDescription }) =>
408 provide(
409 completeSourceCredentialSetup({
410 workspaceId,
411 sourceId,
412 state,
413 code,
414 error,
415 errorDescription,
416 }),
417 ),
418 },
419 },
420 secrets: {
421 list: () => provide(listLocalSecrets()),
422 create: (payload: CreateSecretPayload) => provide(createLocalSecret(payload)),
423 update: (input: { secretId: string; payload: UpdateSecretPayload }) =>
424 provide(updateLocalSecret(input)),
425 remove: (secretId: string) => provide(deleteLocalSecret(secretId)),
426 },
427 policies: {
428 list: () => provide(listPolicies(workspaceId)),
429 create: (payload: CreatePolicyPayload) =>
430 provide(createPolicy({ workspaceId, payload })),
431 get: (policyId: string) =>
432 provide(getPolicy({ workspaceId, policyId: policyId as never })),
433 update: (policyId: string, payload: UpdatePolicyPayload) =>
434 provide(updatePolicy({ workspaceId, policyId: policyId as never, payload })),
435 remove: (policyId: string) =>
436 provide(removePolicy({ workspaceId, policyId: policyId as never })),
437 },
438 sources: {
439 add: (input: ExecutorSourceInput, options?: { baseUrl?: string | null }) =>
440 provideSourceAuth((service) => {
441 const session = createSdkSourceSession();
442 return service.addExecutorSource(
443 {
444 ...input,
445 workspaceId,
446 actorAccountId: accountId,
447 executionId: session.executionId,
448 interactionId: session.interactionId,
449 },
450 options,
451 );
452 }),
453 connect: (payload: ExecutorMcpSourceInput) =>
454 provideSourceAuth((service) =>
455 service.connectMcpSource({
456 ...payload,
457 workspaceId,
458 actorAccountId: accountId,
459 }),
460 ),
461 connectBatch: (payload: ExecutorSourceBatchInput) =>
462 provideSourceAuth((service) => {
463 const session = createSdkSourceSession();
464 return service.connectGoogleDiscoveryBatch({
465 ...payload,
466 workspaceId,
467 actorAccountId: accountId,
468 executionId: session.executionId,
469 interactionId: session.interactionId,
470 });
471 }),
472 discover: (input: {
473 url: string;
474 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
475 }) => provide(discoverSource(input)),
476 list: () => provide(listSources({ workspaceId, accountId })),
477 create: (payload: CreateSourcePayload) =>
478 provide(createSource({ workspaceId, accountId, payload })),
479 get: (sourceId: Source["id"]) =>
480 provide(getSource({ workspaceId, sourceId, accountId })),
481 update: (sourceId: Source["id"], payload: UpdateSourcePayload) =>
482 provide(updateSource({ workspaceId, sourceId, accountId, payload })),
483 remove: (sourceId: Source["id"]) =>
484 provide(removeSource({ workspaceId, sourceId })),
485 inspection: {
486 get: (sourceId: Source["id"]) =>
487 provide(getSourceInspection({ workspaceId, sourceId })),
488 tool: ({ sourceId, toolPath }: { sourceId: Source["id"]; toolPath: string }) =>
489 provide(
490 getSourceInspectionToolDetail({
491 workspaceId,
492 sourceId,
493 toolPath,
494 }),
495 ),
496 discover: ({
497 sourceId,
498 payload,
499 }: {
500 sourceId: Source["id"];
501 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
502 }) =>
503 provide(
504 discoverSourceInspectionTools({
505 workspaceId,
506 sourceId,
507 payload,
508 }),
509 ),
510 },
511 oauthClients: {
512 list: (providerKey: string) =>
513 provideSourceAuth((service) =>
514 service.listWorkspaceOauthClients({
515 workspaceId,
516 providerKey,
517 }),
518 ),
519 create: (payload: CreateWorkspaceOauthClientPayload) =>
520 provideSourceAuth((service) =>
521 service.createWorkspaceOauthClient({
522 workspaceId,
523 providerKey: payload.providerKey,
524 label: payload.label,
525 oauthClient: payload.oauthClient,
526 }),
527 ),
528 remove: (oauthClientId: WorkspaceOauthClient["id"]) =>
529 provideSourceAuth((service) =>
530 service.removeWorkspaceOauthClient({
531 workspaceId,
532 oauthClientId,
533 }),
534 ),
535 },
536 providerGrants: {
537 remove: (grantId: ProviderAuthGrant["id"]) =>
538 provideSourceAuth((service) =>
539 service.removeProviderAuthGrant({
540 workspaceId,
541 grantId,
542 }),
543 ),
544 },
545 },
546 oauth: {
547 startSourceAuth: (input: ExecutorSourceOAuthInput) =>
548 provideSourceAuth((service) =>
549 service.startSourceOAuthSession({
550 ...input,
551 workspaceId,
552 actorAccountId: accountId,
553 }),
554 ),
555 completeSourceAuth: ({ state, code, error, errorDescription }) =>
556 provideSourceAuth((service) =>
557 service.completeSourceOAuthSession({
558 state,
559 code,
560 error,
561 errorDescription,
562 }),
563 ),
564 completeProviderCallback: (input) =>
565 provideSourceAuth((service) =>
566 service.completeProviderOauthCallback({
567 ...input,
568 workspaceId: input.workspaceId ?? workspaceId,
569 actorAccountId: input.actorAccountId ?? accountId,
570 }),
571 ),
572 },
573 executions: {
574 create: (payload: CreateExecutionPayload) =>
575 provide(
576 createExecution({
577 workspaceId,
578 payload,
579 createdByAccountId: accountId,
580 }),
581 ),
582 get: (executionId: Execution["id"]) =>
583 provide(getExecution({ workspaceId, executionId })),
584 resume: (executionId: Execution["id"], payload: ResumeExecutionPayload) =>
585 provide(
586 resumeExecution({
587 workspaceId,
588 executionId,
589 payload,
590 resumedByAccountId: accountId,
591 }),
592 ),
593 },
594 } satisfies Executor["effect"];
595
596 return {
597 runtime,
598 installation,
599 workspaceId,
600 accountId,
601 provide,
602 run,
603 close: () => runtime.close(),
604 effect,
605 local: {
606 installation: () => run(effect.local.installation()),
607 config: () => run(effect.local.config()),
608 credentials: {
609 get: ({ sourceId, interactionId }) =>
610 run(effect.local.credentials.get({ sourceId, interactionId })),
611 submit: ({ sourceId, interactionId, action, token }) =>
612 run(effect.local.credentials.submit({ sourceId, interactionId, action, token })),
613 complete: ({ sourceId, state, code, error, errorDescription }) =>
614 run(effect.local.credentials.complete({ sourceId, state, code, error, errorDescription })),
615 },
616 },
617 secrets: {
618 list: () => run(effect.secrets.list()),
619 create: (payload) => run(effect.secrets.create(payload)),
620 update: (input) => run(effect.secrets.update(input)),
621 remove: (secretId) => run(effect.secrets.remove(secretId)),
622 },
623 policies: {
624 list: () => run(effect.policies.list()),
625 create: (payload) => run(effect.policies.create(payload)),
626 get: (policyId) => run(effect.policies.get(policyId)),
627 update: (policyId, payload) =>
628 run(effect.policies.update(policyId, payload)),
629 remove: async (policyId) =>
630 (await run(effect.policies.remove(policyId))).removed,
631 },
632 sources: {
633 add: (input, options) => run(effect.sources.add(input, options)),
634 connect: (payload) => run(effect.sources.connect(payload)),
635 connectBatch: (payload) => run(effect.sources.connectBatch(payload)),
636 discover: (input) => run(effect.sources.discover(input)),
637 list: () => run(effect.sources.list()),
638 create: (payload) => run(effect.sources.create(payload)),
639 get: (sourceId) => run(effect.sources.get(sourceId)),
640 update: (sourceId, payload) =>
641 run(effect.sources.update(sourceId, payload)),
642 remove: async (sourceId) =>
643 (await run(effect.sources.remove(sourceId))).removed,
644 inspection: {
645 get: (sourceId) => run(effect.sources.inspection.get(sourceId)),
646 tool: ({ sourceId, toolPath }) =>
647 run(effect.sources.inspection.tool({ sourceId, toolPath })),
648 discover: ({ sourceId, payload }) =>
649 run(effect.sources.inspection.discover({ sourceId, payload })),
650 },
651 oauthClients: {
652 list: (providerKey) =>
653 run(effect.sources.oauthClients.list(providerKey)),
654 create: (payload) => run(effect.sources.oauthClients.create(payload)),
655 remove: (oauthClientId) =>
656 run(effect.sources.oauthClients.remove(oauthClientId)),
657 },
658 providerGrants: {
659 remove: (grantId) =>
660 run(effect.sources.providerGrants.remove(grantId)),
661 },
662 },
663 oauth: {
664 startSourceAuth: (input) => run(effect.oauth.startSourceAuth(input)),
665 completeSourceAuth: ({ state, code, error, errorDescription }) =>
666 run(effect.oauth.completeSourceAuth({ state, code, error, errorDescription })),
667 completeProviderCallback: (input) =>
668 run(effect.oauth.completeProviderCallback(input)),
669 },
670 executions: {
671 create: (payload) => run(effect.executions.create(payload)),
672 get: (executionId) => run(effect.executions.get(executionId)),
673 resume: (executionId, payload) =>
674 run(effect.executions.resume(executionId, payload)),
675 },
676 };
677};
678
679export const createExecutorEffect = (
680 options: CreateExecutorOptions,
681): Effect.Effect<Executor, Error> =>
682 Effect.map(
683 options.backend.createRuntime({
684 executionResolver: options.executionResolver,
685 createInternalToolMap: options.createInternalToolMap,
686 resolveSecretMaterial: options.resolveSecretMaterial,
687 getLocalServerBaseUrl: options.getLocalServerBaseUrl,
688 }),
689 fromRuntime,
690 );
691
692export const createExecutor = async (
693 options: CreateExecutorOptions,
694): Promise<Executor> => Effect.runPromise(createExecutorEffect(options));
packages/platform/sdk/src/index.ts+2−12
@@ -1,12 +1,2 @@
1export * from "./errors";1export * from "./backend";
2export * from "./string-schemas";2export * from "./executor";
3export * from "./executions/contracts";
4export * from "./local/contracts";
5export * from "./local/operations";
6export * from "./local/secrets";
7export * from "./policies/contracts";
8export * from "./policies/operations";
9export * from "./sources/contracts";
10export * from "./sources/discovery";
11export * from "./sources/inspection";
12export * from "./sources/operations";
packages/platform/sdk/src/operations.ts+6−0
@@ -0,0 +1,6 @@
1export * from "./local/operations";
2export * from "./local/secrets";
3export * from "./policies/operations";
4export * from "./sources/discovery";
5export * from "./sources/inspection";
6export * from "./sources/operations";
packages/platform/sdk/src/runtime/control-plane-runtime.test.ts+3−3
@@ -23,15 +23,15 @@ import { · 22 unmodified lines
23import type { ToolPath } from "@executor/codemode-core";23import type { ToolPath } from "@executor/codemode-core";
24import { createCatalogImportMetadata } from "@executor/source-core";24import { createCatalogImportMetadata } from "@executor/source-core";
25import { createGraphqlCatalogFragment } from "@executor/source-graphql";25import { createGraphqlCatalogFragment } from "@executor/source-graphql";
26import { createExecutorAdminToolMap } from "../../../internal/src/index";26import { createWorkspaceExecutorAdminToolMap } from "../../../internal/src/index";
27 27
28import {28import {
29 type ControlPlaneRuntime,29 type ControlPlaneRuntime,
30 createControlPlaneRuntime,
31 LiveExecutionManagerService,30 LiveExecutionManagerService,
32 provideControlPlaneRuntime,31 provideControlPlaneRuntime,
33 RuntimeExecutionResolverService,32 RuntimeExecutionResolverService,
34} from "./index";33} from "./index";
34import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../sdk-file/src/index";
35import { createSourceFromPayload } from "./sources/source-definitions";35import { createSourceFromPayload } from "./sources/source-definitions";
36import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions";36import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions";
37import { persistSource } from "./sources/source-store";37import { persistSource } from "./sources/source-store";
@@ -67,7 +67,7 @@ const makeRuntime = Effect.gen(function* () { · 29 unmodified lines
67 workspaceRoot,67 workspaceRoot,
68 homeConfigPath,68 homeConfigPath,
69 homeStateDirectory,69 homeStateDirectory,
70 createInternalToolMap: createExecutorAdminToolMap,70 createInternalToolMap: createWorkspaceExecutorAdminToolMap,
71 }),71 }),
72 (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),72 (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),
73 );73 );
packages/platform/sdk/src/runtime/execution/http.test.ts+1−3
@@ -9,9 +9,7 @@ import * as Schema from "effect/Schema"; · 8 unmodified lines
9import { makeToolInvokerFromTools } from "@executor/codemode-core";9import { makeToolInvokerFromTools } from "@executor/codemode-core";
10import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";10import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
11 11
12import {12import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
13 createControlPlaneRuntime,
14} from "../index";
15import { withControlPlaneClient } from "./test-http-client";13import { withControlPlaneClient } from "./test-http-client";
16 14
17const makeExecutionResolver = () => {15const makeExecutionResolver = () => {
packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts+1−1
@@ -20,9 +20,9 @@ import { discoverMcpToolsFromConnector } from "@executor/source-mcp"; · 19 unmodified lines
20import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";20import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
21 21
22import {22import {
23 createControlPlaneRuntime,
24 type ResolveExecutionEnvironment,23 type ResolveExecutionEnvironment,
25} from "../index";24} from "../index";
25import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
26import { withControlPlaneClient } from "./test-http-client";26import { withControlPlaneClient } from "./test-http-client";
27 27
28type McpFormServer = {28type McpFormServer = {
packages/platform/sdk/src/runtime/execution/runtime-config.test.ts+1−3
@@ -5,9 +5,7 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines
5import { describe, expect, it } from "@effect/vitest";5import { describe, expect, it } from "@effect/vitest";
6import * as Effect from "effect/Effect";6import * as Effect from "effect/Effect";
7 7
8import {8import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
9 createControlPlaneRuntime,
10} from "../index";
11import { withControlPlaneClient } from "./test-http-client";9import { withControlPlaneClient } from "./test-http-client";
12 10
13const writeProjectConfig = (11const writeProjectConfig = (
packages/platform/sdk/src/runtime/execution/test-http-client.ts+12−2
@@ -11,11 +11,21 @@ import * as Effect from "effect/Effect"; · 10 unmodified lines
11import * as Layer from "effect/Layer";11import * as Layer from "effect/Layer";
12 12
13import {13import {
14 createExecutorEffect,
14 type ControlPlaneRuntime,15 type ControlPlaneRuntime,
15} from "../index";16} from "../../index";
16 17
17const createClientLayer = (runtime: ControlPlaneRuntime) => {18const createClientLayer = (runtime: ControlPlaneRuntime) => {
18 const apiLayer = createControlPlaneApiLayer(runtime.runtimeLayer);19 const apiLayer = Layer.unwrapEffect(
20 Effect.map(
21 createExecutorEffect({
22 backend: {
23 createRuntime: () => Effect.succeed(runtime),
24 },
25 }),
26 createControlPlaneApiLayer,
27 ),
28 );
19 29
20 return HttpApiBuilder.serve().pipe(30 return HttpApiBuilder.serve().pipe(
21 Layer.provide(apiLayer),31 Layer.provide(apiLayer),
packages/platform/sdk/src/runtime/execution/workspace/environment.ts+6−0
@@ -26,6 +26,8 @@ import { · 25 unmodified lines
26 type LocalToolRuntime,26 type LocalToolRuntime,
27} from "../../local/tools";27} from "../../local/tools";
28import {28import {
29 InstallationStore,
30 type InstallationStoreShape,
29 SourceArtifactStore,31 SourceArtifactStore,
30 type SourceArtifactStoreShape,32 type SourceArtifactStoreShape,
31 WorkspaceConfigStore,33 WorkspaceConfigStore,
@@ -65,6 +67,7 @@ export const createWorkspaceExecutionEnvironmentResolver = · 33 unmodified lines
65 typeof RuntimeSourceCatalogStoreService67 typeof RuntimeSourceCatalogStoreService
66 >;68 >;
67 localToolRuntimeLoader: LocalToolRuntimeLoaderShape;69 localToolRuntimeLoader: LocalToolRuntimeLoaderShape;
70 installationStore: InstallationStoreShape;
68 workspaceConfigStore: WorkspaceConfigStoreShape;71 workspaceConfigStore: WorkspaceConfigStoreShape;
69 workspaceStateStore: WorkspaceStateStoreShape;72 workspaceStateStore: WorkspaceStateStoreShape;
70 sourceArtifactStore: SourceArtifactStoreShape;73 sourceArtifactStore: SourceArtifactStoreShape;
@@ -92,6 +95,7 @@ export const createWorkspaceExecutionEnvironmentResolver = · 21 unmodified lines
92 sourceStore: input.sourceStore,95 sourceStore: input.sourceStore,
93 sourceCatalogSyncService: input.sourceCatalogSyncService,96 sourceCatalogSyncService: input.sourceCatalogSyncService,
94 sourceCatalogStore: input.sourceCatalogStore,97 sourceCatalogStore: input.sourceCatalogStore,
98 installationStore: input.installationStore,
95 workspaceConfigStore: input.workspaceConfigStore,99 workspaceConfigStore: input.workspaceConfigStore,
96 workspaceStateStore: input.workspaceStateStore,100 workspaceStateStore: input.workspaceStateStore,
97 sourceArtifactStore: input.sourceArtifactStore,101 sourceArtifactStore: input.sourceArtifactStore,
@@ -141,6 +145,7 @@ export const RuntimeExecutionResolverLive = ( · 43 unmodified lines
141 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;145 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
142 const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService;146 const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService;
143 const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService;147 const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService;
148 const installationStore = yield* InstallationStore;
144 const workspaceConfigStore = yield* WorkspaceConfigStore;149 const workspaceConfigStore = yield* WorkspaceConfigStore;
145 const workspaceStateStore = yield* WorkspaceStateStore;150 const workspaceStateStore = yield* WorkspaceStateStore;
146 const sourceArtifactStore = yield* SourceArtifactStore;151 const sourceArtifactStore = yield* SourceArtifactStore;
@@ -153,6 +158,7 @@ export const RuntimeExecutionResolverLive = ( · 6 unmodified lines
153 sourceAuthMaterialService,158 sourceAuthMaterialService,
154 sourceCatalogStore,159 sourceCatalogStore,
155 localToolRuntimeLoader,160 localToolRuntimeLoader,
161 installationStore,
156 workspaceConfigStore,162 workspaceConfigStore,
157 workspaceStateStore,163 workspaceStateStore,
158 sourceArtifactStore,164 sourceArtifactStore,
packages/platform/sdk/src/runtime/execution/workspace/tool-invoker.ts+8−0
@@ -16,6 +16,7 @@ import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; · 15 unmodified lines
16import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context";16import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context";
17import { type LocalToolRuntime } from "../../local/tools";17import { type LocalToolRuntime } from "../../local/tools";
18import {18import {
19 type InstallationStoreShape,
19 makeWorkspaceStorageLayer,20 makeWorkspaceStorageLayer,
20 type SourceArtifactStoreShape,21 type SourceArtifactStoreShape,
21 type WorkspaceConfigStoreShape,22 type WorkspaceConfigStoreShape,
@@ -47,6 +48,7 @@ export type WorkspaceInternalToolContext = { · 25 unmodified lines
47 typeof RuntimeSourceCatalogSyncService48 typeof RuntimeSourceCatalogSyncService
48 >;49 >;
49 sourceAuthService: RuntimeSourceAuthService;50 sourceAuthService: RuntimeSourceAuthService;
51 installationStore: InstallationStoreShape;
50 workspaceConfigStore: WorkspaceConfigStoreShape;52 workspaceConfigStore: WorkspaceConfigStoreShape;
51 workspaceStateStore: WorkspaceStateStoreShape;53 workspaceStateStore: WorkspaceStateStoreShape;
52 sourceArtifactStore: SourceArtifactStoreShape;54 sourceArtifactStore: SourceArtifactStoreShape;
@@ -68,6 +70,7 @@ export const createWorkspaceToolInvoker = (input: { · 15 unmodified lines
68 sourceCatalogStore: Effect.Effect.Success<70 sourceCatalogStore: Effect.Effect.Success<
69 typeof RuntimeSourceCatalogStoreService71 typeof RuntimeSourceCatalogStoreService
70 >;72 >;
73 installationStore: InstallationStoreShape;
71 workspaceConfigStore: WorkspaceConfigStoreShape;74 workspaceConfigStore: WorkspaceConfigStoreShape;
72 workspaceStateStore: WorkspaceStateStoreShape;75 workspaceStateStore: WorkspaceStateStoreShape;
73 sourceArtifactStore: SourceArtifactStoreShape;76 sourceArtifactStore: SourceArtifactStoreShape;
@@ -97,6 +100,10 @@ export const createWorkspaceToolInvoker = (input: { · 23 unmodified lines
97 workspaceId: input.workspaceId,100 workspaceId: input.workspaceId,
98 accountId: input.accountId,101 accountId: input.accountId,
99 sourceAuthService: input.sourceAuthService,102 sourceAuthService: input.sourceAuthService,
103 installationStore: input.installationStore,
104 workspaceConfigStore: input.workspaceConfigStore,
105 workspaceStateStore: input.workspaceStateStore,
106 sourceArtifactStore: input.sourceArtifactStore,
100 runtimeLocalWorkspace: input.runtimeLocalWorkspace,107 runtimeLocalWorkspace: input.runtimeLocalWorkspace,
101 });108 });
102 const internalTools =109 const internalTools =
@@ -107,6 +114,7 @@ export const createWorkspaceToolInvoker = (input: { · 4 unmodified lines
107 sourceStore: input.sourceStore,114 sourceStore: input.sourceStore,
108 sourceCatalogSyncService: input.sourceCatalogSyncService,115 sourceCatalogSyncService: input.sourceCatalogSyncService,
109 sourceAuthService: input.sourceAuthService,116 sourceAuthService: input.sourceAuthService,
117 installationStore: input.installationStore,
110 workspaceConfigStore: input.workspaceConfigStore,118 workspaceConfigStore: input.workspaceConfigStore,
111 workspaceStateStore: input.workspaceStateStore,119 workspaceStateStore: input.workspaceStateStore,
112 sourceArtifactStore: input.sourceArtifactStore,120 sourceArtifactStore: input.sourceArtifactStore,
packages/platform/sdk/src/runtime/index.ts+207−113
@@ -1,51 +1,66 @@
1import { FileSystem } from "@effect/platform";1import {
2 createToolCatalogFromTools,
3 makeToolInvokerFromTools,
4 type ToolMap,
5} from "@executor/codemode-core";
6import { clearAllMcpConnectionPools } from "@executor/source-mcp";
7import type { LocalInstallation } from "#schema";
2import * as Effect from "effect/Effect";8import * as Effect from "effect/Effect";
3import * as Exit from "effect/Exit";
4import * as Layer from "effect/Layer";9import * as Layer from "effect/Layer";
5import * as ManagedRuntime from "effect/ManagedRuntime";10import * as ManagedRuntime from "effect/ManagedRuntime";
6import * as Scope from "effect/Scope";
7import { NodeFileSystem } from "@effect/platform-node";
8import { clearAllMcpConnectionPools } from "@executor/source-mcp";
9import type { LocalInstallation } from "#schema";
10 11
11import { type ResolveExecutionEnvironment } from "./execution/state";12import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material";
12import {13import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime";
13 createLiveExecutionManager,14import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile";
14 LiveExecutionManagerService,15import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync";
15} from "./execution/live";16import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live";
16import {17import { RuntimeExecutionResolverLive } from "./execution/workspace/environment";
17 createLocalControlPlanePersistence,18import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker";
18 type LocalControlPlanePersistence,19import type {
19} from "./local/control-plane-store";20 LoadedLocalExecutorConfig,
20 21 ResolvedLocalWorkspaceContext,
21import { resolveLocalWorkspaceContext } from "./local/config";22} from "./local/config";
23import type { LocalExecutorConfig } from "#schema";
24import type { LocalControlPlanePersistence } from "./local/control-plane-store";
22import {25import {
23 LocalStorageLive,26 type DeleteSecretMaterial,
24 LocalInstallationStore,27 type ResolveSecretMaterial,
25 LocalWorkspaceConfigStore,28 SecretMaterialDeleterService,
26} from "./local/storage";29 SecretMaterialResolverService,
30 SecretMaterialStorerService,
31 SecretMaterialUpdaterService,
32 type StoreSecretMaterial,
33 type UpdateSecretMaterial,
34} from "./local/secret-material-providers";
35import type { LocalSourceArtifact } from "./local/source-artifacts";
27import {36import {
28 type RuntimeLocalWorkspaceState,37 type RuntimeLocalWorkspaceState,
29 RuntimeLocalWorkspaceLive,38 RuntimeLocalWorkspaceLive,
30} from "./local/runtime-context";39} from "./local/runtime-context";
31import { LocalToolRuntimeLoaderLive } from "./local/tools";40import {
41 type LocalToolRuntime,
42 type LocalToolRuntimeLoaderShape,
43 LocalToolRuntimeLoaderService,
44} from "./local/tools";
45import {
46 InstallationStore,
47 makeLocalStorageLayer,
48 type InstallationStoreShape,
49 type SourceArtifactStoreShape,
50 type WorkspaceConfigStoreShape,
51 type WorkspaceStateStoreShape,
52} from "./local/storage";
53import type { LocalWorkspaceState } from "./local/workspace-state";
32import { synchronizeLocalWorkspaceState } from "./local/workspace-sync";54import { synchronizeLocalWorkspaceState } from "./local/workspace-sync";
33import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store";55import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store";
34import { RuntimeSourceStoreLive } from "./sources/source-store";
35import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime";
36import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile";
37import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material";
38import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync";
39import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service";56import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service";
40import type { ResolveSecretMaterial } from "./local/secret-material-providers";57import { RuntimeSourceStoreLive } from "./sources/source-store";
41import { SecretMaterialLive } from "./local/secret-material-providers";
42import { RuntimeExecutionResolverLive } from "./execution/workspace/environment";
43import type { CreateWorkspaceInternalToolMap } from "./execution/workspace/tool-invoker";
44 58
45export * from "./execution/state";59export * from "./execution/state";
46export * from "./sources/executor-tools";60export * from "./sources/executor-tools";
47export * from "./execution/live";61export * from "./execution/live";
48export * from "./local/config";62export * from "./local/config";
63export * from "./local/control-plane-store";
49export * from "./local/errors";64export * from "./local/errors";
50export * from "./local/installation";65export * from "./local/installation";
51export * from "./local/runtime-context";66export * from "./local/runtime-context";
@@ -89,54 +104,157 @@ export type RuntimeControlPlaneOptions = { · 37 unmodified lines
89 createInternalToolMap?: CreateWorkspaceInternalToolMap;104 createInternalToolMap?: CreateWorkspaceInternalToolMap;
90 resolveSecretMaterial?: ResolveSecretMaterial;105 resolveSecretMaterial?: ResolveSecretMaterial;
91 getLocalServerBaseUrl?: () => string | undefined;106 getLocalServerBaseUrl?: () => string | undefined;
92 localDataDir?: string;
93 workspaceRoot?: string;
94 homeConfigPath?: string;
95 homeStateDirectory?: string;
96};107};
97 108
109type ResolveExecutionEnvironment = import("./execution/state").ResolveExecutionEnvironment;
110
98const detailsFromCause = (cause: unknown): string =>111const detailsFromCause = (cause: unknown): string =>
99 cause instanceof Error ? cause.message : String(cause);112 cause instanceof Error ? cause.message : String(cause);
100 113
101const toLocalRuntimeBootstrapError = (cause: unknown): Error => {114const toRuntimeBootstrapError = (cause: unknown): Error => {
102 const details = detailsFromCause(cause);115 const details = detailsFromCause(cause);
103 return new Error(`Failed initializing local runtime: ${details}`);116 return new Error(`Failed initializing runtime: ${details}`);
104};117};
105 118
106const closeScope = (scope: Scope.CloseableScope) =>
107 Scope.close(scope, Exit.void).pipe(Effect.orDie);
108
109export type RuntimeControlPlaneLayer = Layer.Layer<any, never, never>;119export type RuntimeControlPlaneLayer = Layer.Layer<any, never, never>;
110 120
121export type BoundInstallationStore = {
122 load: () => Effect.Effect<LocalInstallation, Error, never>;
123 getOrProvision: () => Effect.Effect<LocalInstallation, Error, never>;
124};
125
126export type BoundWorkspaceConfigStore = {
127 load: () => Effect.Effect<LoadedLocalExecutorConfig, Error, never>;
128 writeProject: (
129 config: LocalExecutorConfig,
130 ) => Effect.Effect<void, Error, never>;
131 resolveRelativePath: WorkspaceConfigStoreShape["resolveRelativePath"];
132};
133
134export type BoundWorkspaceStateStore = {
135 load: () => Effect.Effect<LocalWorkspaceState, Error, never>;
136 write: (
137 state: LocalWorkspaceState,
138 ) => Effect.Effect<void, Error, never>;
139};
140
141export type BoundSourceArtifactStore = {
142 build: SourceArtifactStoreShape["build"];
143 read: (
144 sourceId: string,
145 ) => Effect.Effect<LocalSourceArtifact | null, Error, never>;
146 write: (input: {
147 sourceId: string;
148 artifact: LocalSourceArtifact;
149 }) => Effect.Effect<void, Error, never>;
150 remove: (sourceId: string) => Effect.Effect<void, Error, never>;
151};
152
153export type BoundLocalToolRuntimeLoader = {
154 load: () => ReturnType<LocalToolRuntimeLoaderShape["load"]>;
155};
156
157export type RuntimeSecretMaterialServices = {
158 resolve: ResolveSecretMaterial;
159 store: StoreSecretMaterial;
160 delete: DeleteSecretMaterial;
161 update: UpdateSecretMaterial;
162};
163
164export type RuntimeControlPlaneServices = {
165 workspaceContext: ResolvedLocalWorkspaceContext;
166 installationStore: BoundInstallationStore;
167 workspaceConfigStore: BoundWorkspaceConfigStore;
168 workspaceStateStore: BoundWorkspaceStateStore;
169 sourceArtifactStore: BoundSourceArtifactStore;
170 localToolRuntimeLoader?: BoundLocalToolRuntimeLoader;
171 secretMaterial: RuntimeSecretMaterialServices;
172 persistence: LocalControlPlanePersistence;
173};
174
175const emptyToolRuntime = (): LocalToolRuntime => {
176 const tools: ToolMap = {};
177 return {
178 tools,
179 catalog: createToolCatalogFromTools({ tools }),
180 toolInvoker: makeToolInvokerFromTools({ tools }),
181 toolPaths: new Set(),
182 };
183};
184
185const toInstallationStoreShape = (
186 input: BoundInstallationStore,
187): InstallationStoreShape => ({
188 load: () => input.load(),
189 getOrProvision: () => input.getOrProvision(),
190});
191
192const toWorkspaceConfigStoreShape = (
193 input: BoundWorkspaceConfigStore,
194): WorkspaceConfigStoreShape => ({
195 load: () => input.load(),
196 writeProject: ({ config }) => input.writeProject(config),
197 resolveRelativePath: input.resolveRelativePath,
198});
199
200const toWorkspaceStateStoreShape = (
201 input: BoundWorkspaceStateStore,
202): WorkspaceStateStoreShape => ({
203 load: () => input.load(),
204 write: ({ state }) => input.write(state),
205});
206
207const toSourceArtifactStoreShape = (
208 input: BoundSourceArtifactStore,
209): SourceArtifactStoreShape => ({
210 build: input.build,
211 read: ({ sourceId }) => input.read(sourceId),
212 write: ({ sourceId, artifact }) => input.write({ sourceId, artifact }),
213 remove: ({ sourceId }) => input.remove(sourceId),
214});
215
216const makeSecretMaterialLayer = (input: RuntimeSecretMaterialServices) =>
217 Layer.mergeAll(
218 Layer.succeed(SecretMaterialResolverService, input.resolve),
219 Layer.succeed(SecretMaterialStorerService, input.store),
220 Layer.succeed(SecretMaterialDeleterService, input.delete),
221 Layer.succeed(SecretMaterialUpdaterService, input.update),
222 );
223
111export const createRuntimeControlPlaneLayer = (224export const createRuntimeControlPlaneLayer = (
112 input: RuntimeControlPlaneOptions & {225 input: RuntimeControlPlaneOptions & RuntimeControlPlaneServices & {
113 store: ControlPlaneStoreShape;226 store: ControlPlaneStoreShape;
114 localWorkspaceState: RuntimeLocalWorkspaceState;227 localWorkspaceState: RuntimeLocalWorkspaceState;
115 liveExecutionManager: ReturnType<typeof createLiveExecutionManager>;228 liveExecutionManager: ReturnType<typeof createLiveExecutionManager>;
116 },229 },
117) => {230) => {
118 const platformLayer = NodeFileSystem.layer;231 const storageLayer = makeLocalStorageLayer({
119 const storageLayer = LocalStorageLive.pipe(Layer.provide(platformLayer));232 installationStore: toInstallationStoreShape(input.installationStore),
120 const localToolRuntimeLayer = LocalToolRuntimeLoaderLive.pipe(233 workspaceConfigStore: toWorkspaceConfigStoreShape(input.workspaceConfigStore),
121 Layer.provide(platformLayer),234 workspaceStateStore: toWorkspaceStateStoreShape(input.workspaceStateStore),
235 sourceArtifactStore: toSourceArtifactStoreShape(input.sourceArtifactStore),
236 });
237 const localToolRuntimeLayer = Layer.succeed(
238 LocalToolRuntimeLoaderService,
239 LocalToolRuntimeLoaderService.of({
240 load: () =>
241 input.localToolRuntimeLoader?.load() ?? Effect.succeed(emptyToolRuntime()),
242 }),
122 );243 );
123 244
124 const baseLayer = Layer.mergeAll(245 const baseLayer = Layer.mergeAll(
125 platformLayer,
126 Layer.succeed(ControlPlaneStore, input.store),246 Layer.succeed(ControlPlaneStore, input.store),
127 RuntimeLocalWorkspaceLive(input.localWorkspaceState),247 RuntimeLocalWorkspaceLive(input.localWorkspaceState),
128 storageLayer,248 storageLayer,
129 Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager),249 Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager),
130 );250 );
131 251
132 const secretMaterialLayer = SecretMaterialLive({252 const secretMaterialLayer = makeSecretMaterialLayer(input.secretMaterial).pipe(
133 resolveSecretMaterial: input.resolveSecretMaterial,
134 }).pipe(Layer.provide(baseLayer));
135
136 const sourceStoreLayer = RuntimeSourceStoreLive.pipe(
137 Layer.provide(baseLayer),253 Layer.provide(baseLayer),
138 );254 );
139 255
256 const sourceStoreLayer = RuntimeSourceStoreLive.pipe(Layer.provide(baseLayer));
257
140 const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe(258 const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe(
141 Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)),259 Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)),
142 );260 );
@@ -203,73 +321,50 @@ export type ControlPlaneRuntime = { · 60 unmodified lines
203 close: () => Promise<void>;321 close: () => Promise<void>;
204};322};
205 323
206export type CreateControlPlaneRuntimeOptions = RuntimeControlPlaneOptions;
207
208export const provideControlPlaneRuntime = <A, E, R>(324export const provideControlPlaneRuntime = <A, E, R>(
209 effect: Effect.Effect<A, E, R>,325 effect: Effect.Effect<A, E, R>,
210 runtime: ControlPlaneRuntime,326 runtime: ControlPlaneRuntime,
211): Effect.Effect<A, E | never, any> =>327): Effect.Effect<A, E | never, never> =>
212 effect.pipe(Effect.provide(runtime.managedRuntime));328 effect.pipe(Effect.provide(runtime.managedRuntime));
213 329
214export const createControlPlaneRuntime = (330export const createControlPlaneRuntimeFromServices = (input: {
215 options: CreateControlPlaneRuntimeOptions,331 services: RuntimeControlPlaneServices;
216): Effect.Effect<ControlPlaneRuntime, Error> =>332} & RuntimeControlPlaneOptions): Effect.Effect<ControlPlaneRuntime, Error> =>
217 (Effect.gen(function* () {333 (Effect.gen(function* () {
218 const scope = yield* Scope.make();334 const localInstallation = yield* input.services.installationStore
335 .getOrProvision()
336 .pipe(Effect.mapError(toRuntimeBootstrapError));
219 337
220 const localWorkspaceContext = yield* resolveLocalWorkspaceContext({338 const loadedLocalConfig = yield* input.services.workspaceConfigStore
221 workspaceRoot: options.workspaceRoot,339 .load()
222 homeConfigPath: options.homeConfigPath,340 .pipe(Effect.mapError(toRuntimeBootstrapError));
223 homeStateDirectory: options.homeStateDirectory,
224 }).pipe(
225 Effect.mapError(toLocalRuntimeBootstrapError),
226 Effect.catchAll((error) =>
227 closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))),
228 ),
229 );
230
231 const installationStore = LocalInstallationStore;
232 const workspaceConfigStore = LocalWorkspaceConfigStore;
233 const fileSystem = yield* FileSystem.FileSystem;
234
235 const localInstallation = yield* installationStore
236 .getOrProvision({
237 context: localWorkspaceContext,
238 })
239 .pipe(
240 Effect.mapError(toLocalRuntimeBootstrapError),
241 Effect.catchAll((error) =>
242 closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))),
243 ),
244 );
245
246 const persistence = createLocalControlPlanePersistence(
247 localWorkspaceContext,
248 fileSystem,
249 );
250 const rows = persistence.rows;
251
252 const loadedLocalConfig = yield* workspaceConfigStore
253 .load(localWorkspaceContext)
254 .pipe(
255 Effect.mapError(toLocalRuntimeBootstrapError),
256 Effect.catchAll((error) =>
257 closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))),
258 ),
259 );
260 341
261 const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({342 const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({
262 context: localWorkspaceContext,343 context: input.services.workspaceContext,
263 loadedConfig: loadedLocalConfig,344 loadedConfig: loadedLocalConfig,
264 }).pipe(345 })
265 Effect.mapError(toLocalRuntimeBootstrapError),346 .pipe(
266 Effect.catchAll((error) =>347 Effect.provide(
267 closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))),348 makeLocalStorageLayer({
268 ),349 installationStore: toInstallationStoreShape(
269 );350 input.services.installationStore,
351 ),
352 workspaceConfigStore: toWorkspaceConfigStoreShape(
353 input.services.workspaceConfigStore,
354 ),
355 workspaceStateStore: toWorkspaceStateStoreShape(
356 input.services.workspaceStateStore,
357 ),
358 sourceArtifactStore: toSourceArtifactStoreShape(
359 input.services.sourceArtifactStore,
360 ),
361 }),
362 ),
363 )
364 .pipe(Effect.mapError(toRuntimeBootstrapError));
270 365
271 const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = {366 const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = {
272 context: localWorkspaceContext,367 context: input.services.workspaceContext,
273 installation: {368 installation: {
274 workspaceId: localInstallation.workspaceId,369 workspaceId: localInstallation.workspaceId,
275 accountId: localInstallation.accountId,370 accountId: localInstallation.accountId,
@@ -282,8 +377,9 @@ export const createControlPlaneRuntime = ( · 6 unmodified lines
282 const liveExecutionManager = createLiveExecutionManager();377 const liveExecutionManager = createLiveExecutionManager();
283 378
284 const concreteRuntimeLayer = createRuntimeControlPlaneLayer({379 const concreteRuntimeLayer = createRuntimeControlPlaneLayer({
285 ...options,380 ...input,
286 store: rows,381 ...input.services,
382 store: input.services.persistence.rows,
287 localWorkspaceState: runtimeLocalWorkspaceState,383 localWorkspaceState: runtimeLocalWorkspaceState,
288 liveExecutionManager,384 liveExecutionManager,
289 });385 });
@@ -298,7 +394,7 @@ export const createControlPlaneRuntime = ( · 8 unmodified lines
298 );394 );
299 395
300 return {396 return {
301 persistence,397 persistence: input.services.persistence,
302 localInstallation,398 localInstallation,
303 managedRuntime,399 managedRuntime,
304 runtimeLayer: concreteRuntimeLayer as RuntimeControlPlaneLayer,400 runtimeLayer: concreteRuntimeLayer as RuntimeControlPlaneLayer,
@@ -307,9 +403,7 @@ export const createControlPlaneRuntime = ( · 2 unmodified lines
307 () => undefined,403 () => undefined,
308 );404 );
309 await managedRuntime.dispose().catch(() => undefined);405 await managedRuntime.dispose().catch(() => undefined);
406 await input.services.persistence.close().catch(() => undefined);
310 },407 },
311 };408 };
312 }).pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect<409 }) as Effect.Effect<ControlPlaneRuntime, Error>);
313 ControlPlaneRuntime,
314 Error
315 >);
packages/platform/sdk/src/runtime/local/installation.test.ts+1−1
@@ -5,7 +5,7 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines
5import { describe, expect, it } from "@effect/vitest";5import { describe, expect, it } from "@effect/vitest";
6import * as Effect from "effect/Effect";6import * as Effect from "effect/Effect";
7 7
8import { createControlPlaneRuntime } from "../index";8import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
9import { getOrProvisionLocalInstallation } from "./installation";9import { getOrProvisionLocalInstallation } from "./installation";
10import { resolveLocalWorkspaceContext } from "./config";10import { resolveLocalWorkspaceContext } from "./config";
11 11
packages/platform/sdk/src/runtime/local/storage.ts+2−2
@@ -38,10 +38,10 @@ import type { Source } from "#schema"; · 37 unmodified lines
38export type InstallationStoreShape = {38export type InstallationStoreShape = {
39 load: (39 load: (
40 context: ResolvedLocalWorkspaceContext,40 context: ResolvedLocalWorkspaceContext,
41 ) => Effect.Effect<LocalInstallation, never, never>;41 ) => Effect.Effect<LocalInstallation, Error, never>;
42 getOrProvision: (input: {42 getOrProvision: (input: {
43 context: ResolvedLocalWorkspaceContext;43 context: ResolvedLocalWorkspaceContext;
44 }) => Effect.Effect<LocalInstallation, never, never>;44 }) => Effect.Effect<LocalInstallation, Error, never>;
45};45};
46 46
47export class InstallationStore extends Context.Tag(47export class InstallationStore extends Context.Tag(
packages/platform/sdk/src/runtime/sources/executor-tools.ts+45−10
@@ -22,10 +22,10 @@ import * as Schema from "effect/Schema"; · 21 unmodified lines
22import type { RuntimeLocalWorkspaceState } from "../local/runtime-context";22import type { RuntimeLocalWorkspaceState } from "../local/runtime-context";
23import {23import {
24 type LocalStorageServices,24 type LocalStorageServices,
25 LocalInstallationStore,25 type InstallationStoreShape,
26 LocalSourceArtifactStore,26 type SourceArtifactStoreShape,
27 LocalWorkspaceConfigStore,27 type WorkspaceConfigStoreShape,
28 LocalWorkspaceStateStore,28 type WorkspaceStateStoreShape,
29 makeLocalStorageLayer,29 makeLocalStorageLayer,
30} from "../local/storage";30} from "../local/storage";
31import { provideOptionalRuntimeLocalWorkspace } from "../local/runtime-context";31import { provideOptionalRuntimeLocalWorkspace } from "../local/runtime-context";
@@ -34,14 +34,15 @@ import { runtimeEffectError } from "../effect-errors"; · 2 unmodified lines
34/** Run an Effect as a Promise, preserving the original error (not FiberFailure). */34/** Run an Effect as a Promise, preserving the original error (not FiberFailure). */
35const runEffect = async <A>(35const runEffect = async <A>(
36 effect: Effect.Effect<A, unknown, LocalStorageServices>,36 effect: Effect.Effect<A, unknown, LocalStorageServices>,
37 storage: {
38 installationStore: InstallationStoreShape;
39 workspaceConfigStore: WorkspaceConfigStoreShape;
40 workspaceStateStore: WorkspaceStateStoreShape;
41 sourceArtifactStore: SourceArtifactStoreShape;
42 },
37 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null,43 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null,
38): Promise<A> => {44): Promise<A> => {
39 const baseLayer = makeLocalStorageLayer({45 const baseLayer = makeLocalStorageLayer(storage);
40 installationStore: LocalInstallationStore,
41 workspaceConfigStore: LocalWorkspaceConfigStore,
42 workspaceStateStore: LocalWorkspaceStateStore,
43 sourceArtifactStore: LocalSourceArtifactStore,
44 });
45 const exit = await Effect.runPromiseExit(46 const exit = await Effect.runPromiseExit(
46 provideOptionalRuntimeLocalWorkspace(47 provideOptionalRuntimeLocalWorkspace(
47 effect.pipe(Effect.provide(baseLayer)),48 effect.pipe(Effect.provide(baseLayer)),
@@ -314,6 +315,10 @@ export const createExecutorToolMap = (input: { · 266 unmodified lines
314 workspaceId: WorkspaceId;315 workspaceId: WorkspaceId;
315 accountId: AccountId;316 accountId: AccountId;
316 sourceAuthService: RuntimeSourceAuthService;317 sourceAuthService: RuntimeSourceAuthService;
318 installationStore: InstallationStoreShape;
319 workspaceConfigStore: WorkspaceConfigStoreShape;
320 workspaceStateStore: WorkspaceStateStoreShape;
321 sourceArtifactStore: SourceArtifactStoreShape;
317 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;322 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
318}): ToolMap => ({323}): ToolMap => ({
319 "executor.sources.add": toTool({324 "executor.sources.add": toTool({
@@ -349,6 +354,12 @@ export const createExecutorToolMap = (input: { · 29 unmodified lines
349 }354 }
350 : undefined,355 : undefined,
351 ),356 ),
357 {
358 installationStore: input.installationStore,
359 workspaceConfigStore: input.workspaceConfigStore,
360 workspaceStateStore: input.workspaceStateStore,
361 sourceArtifactStore: input.sourceArtifactStore,
362 },
352 input.runtimeLocalWorkspace,363 input.runtimeLocalWorkspace,
353 );364 );
354 365
@@ -382,6 +393,12 @@ export const createExecutorToolMap = (input: { · 27 unmodified lines
382 invocation: context?.invocation,393 invocation: context?.invocation,
383 onElicitation: context?.onElicitation,394 onElicitation: context?.onElicitation,
384 }),395 }),
396 {
397 installationStore: input.installationStore,
398 workspaceConfigStore: input.workspaceConfigStore,
399 workspaceStateStore: input.workspaceStateStore,
400 sourceArtifactStore: input.sourceArtifactStore,
401 },
385 input.runtimeLocalWorkspace,402 input.runtimeLocalWorkspace,
386 );403 );
387 404
@@ -412,6 +429,12 @@ export const createExecutorToolMap = (input: { · 24 unmodified lines
412 }429 }
413 : undefined,430 : undefined,
414 ),431 ),
432 {
433 installationStore: input.installationStore,
434 workspaceConfigStore: input.workspaceConfigStore,
435 workspaceStateStore: input.workspaceStateStore,
436 sourceArtifactStore: input.sourceArtifactStore,
437 },
415 input.runtimeLocalWorkspace,438 input.runtimeLocalWorkspace,
416 );439 );
417 440
@@ -456,6 +479,12 @@ export const createExecutorToolMap = (input: { · 38 unmodified lines
456 elicitationId: result.sessionId,479 elicitationId: result.sessionId,
457 },480 },
458 }),481 }),
482 {
483 installationStore: input.installationStore,
484 workspaceConfigStore: input.workspaceConfigStore,
485 workspaceStateStore: input.workspaceStateStore,
486 sourceArtifactStore: input.sourceArtifactStore,
487 },
459 input.runtimeLocalWorkspace,488 input.runtimeLocalWorkspace,
460 );489 );
461 490
@@ -469,6 +498,12 @@ export const createExecutorToolMap = (input: { · 7 unmodified lines
469 sourceId: result.source.id,498 sourceId: result.source.id,
470 actorAccountId: input.accountId,499 actorAccountId: input.accountId,
471 }),500 }),
501 {
502 installationStore: input.installationStore,
503 workspaceConfigStore: input.workspaceConfigStore,
504 workspaceStateStore: input.workspaceStateStore,
505 sourceArtifactStore: input.sourceArtifactStore,
506 },
472 input.runtimeLocalWorkspace,507 input.runtimeLocalWorkspace,
473 );508 );
474 return toSerializableValue(connected);509 return toSerializableValue(connected);
packages/platform/sdk/src/schema/index.ts+0−1
@@ -2,7 +2,6 @@ export * from "./common"; · 1 unmodified line
2export * from "./enums";2export * from "./enums";
3export * from "./ids";3export * from "./ids";
4 4
5export * from "./models/auth";
6export * from "./models/source";5export * from "./models/source";
7export * from "./models/source-catalog";6export * from "./models/source-catalog";
8export * from "./models/auth-artifact";7export * from "./models/auth-artifact";
packages/platform/sdk/src/schema/models/auth/account.ts+0−29
@@ -1,29 +0,0 @@
1import { Schema } from "effect";
2
3import { TimestampMsSchema } from "../../common";
4import { AccountIdSchema } from "../../ids";
5import { PrincipalProviderSchema } from "./principal";
6
7export const AccountSchema = Schema.Struct({
8 id: AccountIdSchema,
9 provider: PrincipalProviderSchema,
10 subject: Schema.String,
11 email: Schema.NullOr(Schema.String),
12 displayName: Schema.NullOr(Schema.String),
13 createdAt: TimestampMsSchema,
14 updatedAt: TimestampMsSchema,
15});
16
17export const AccountInsertSchema = AccountSchema;
18
19export const AccountUpdateSchema = Schema.partial(
20 Schema.Struct({
21 provider: PrincipalProviderSchema,
22 subject: Schema.String,
23 email: Schema.NullOr(Schema.String),
24 displayName: Schema.NullOr(Schema.String),
25 updatedAt: TimestampMsSchema,
26 }),
27);
28
29export type Account = typeof AccountSchema.Type;
packages/platform/sdk/src/schema/models/auth/index.ts+0−2
@@ -1,2 +0,0 @@
1export * from "./account";
2export * from "./principal";
packages/platform/sdk/src/schema/models/auth/principal.ts+0−20
@@ -1,20 +0,0 @@
1import { Schema } from "effect";
2
3import { AccountIdSchema } from "../../ids";
4
5export const PrincipalProviderSchema = Schema.Literal(
6 "local",
7 "workos",
8 "service",
9);
10
11export const PrincipalSchema = Schema.Struct({
12 accountId: AccountIdSchema,
13 provider: PrincipalProviderSchema,
14 subject: Schema.String,
15 email: Schema.NullOr(Schema.String),
16 displayName: Schema.NullOr(Schema.String),
17});
18
19export type PrincipalProvider = typeof PrincipalProviderSchema.Type;
20export type Principal = typeof PrincipalSchema.Type;
packages/platform/server/package.json+1−0
@@ -18,6 +18,7 @@ · 17 unmodified lines
18 "@executor/platform-api": "workspace:*",18 "@executor/platform-api": "workspace:*",
19 "@executor/platform-internal": "workspace:*",19 "@executor/platform-internal": "workspace:*",
20 "@executor/platform-sdk": "workspace:*",20 "@executor/platform-sdk": "workspace:*",
21 "@executor/platform-sdk-file": "workspace:*",
21 "@opentelemetry/api": "^1.9.0",22 "@opentelemetry/api": "^1.9.0",
22 "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",23 "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
23 "@opentelemetry/resources": "^2.6.0",24 "@opentelemetry/resources": "^2.6.0",
packages/platform/server/src/index.ts+32−19
@@ -9,12 +9,16 @@ import { Readable } from "node:stream"; · 8 unmodified lines
9import { FileSystem, HttpApiBuilder, HttpServer } from "@effect/platform";9import { FileSystem, HttpApiBuilder, HttpServer } from "@effect/platform";
10import { NodeFileSystem } from "@effect/platform-node";10import { NodeFileSystem } from "@effect/platform-node";
11import {11import {
12 createControlPlaneApiLayer,12 createExecutorApiLayer,
13} from "@executor/platform-api";13} from "@executor/platform-api";
14import { createExecutorMcpRequestHandler } from "@executor/executor-mcp";14import { createExecutorMcpRequestHandler } from "@executor/executor-mcp";
15import { createExecutorAdminToolMap } from "@executor/platform-internal";15import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
16import {
17 createExecutorEffect,
18 type Executor,
19} from "@executor/platform-sdk";
20import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
16import {21import {
17 createControlPlaneRuntime,
18 type ResolveExecutionEnvironment,22 type ResolveExecutionEnvironment,
19 type ResolveSecretMaterial,23 type ResolveSecretMaterial,
20 type ControlPlaneRuntime,24 type ControlPlaneRuntime,
@@ -71,6 +75,7 @@ type StaticUiOptions = { · 50 unmodified lines
71};75};
72 76
73export type LocalExecutorServer = {77export type LocalExecutorServer = {
78 readonly executor: Executor;
74 readonly runtime: ControlPlaneRuntime;79 readonly runtime: ControlPlaneRuntime;
75 readonly port: number;80 readonly port: number;
76 readonly host: string;81 readonly host: string;
@@ -89,6 +94,7 @@ export type StartLocalExecutorServerOptions = { · 12 unmodified lines
89};94};
90 95
91export type LocalExecutorRequestHandler = {96export type LocalExecutorRequestHandler = {
97 readonly executor: Executor;
92 readonly runtime: ControlPlaneRuntime;98 readonly runtime: ControlPlaneRuntime;
93 readonly handleApiRequest: (request: Request) => Promise<Response>;99 readonly handleApiRequest: (request: Request) => Promise<Response>;
94 readonly getBaseUrl: () => string | undefined;100 readonly getBaseUrl: () => string | undefined;
@@ -102,25 +108,27 @@ const EXECUTOR_NPM_DIST_TAGS_PATHNAME = "/v1/app/npm/dist-tags"; · 7 unmodified lines
102const EXECUTOR_NPM_DIST_TAGS_URL =108const EXECUTOR_NPM_DIST_TAGS_URL =
103 "https://registry.npmjs.org/-/package/executor/dist-tags";109 "https://registry.npmjs.org/-/package/executor/dist-tags";
104 110
105const disposeRuntime = (runtime: ControlPlaneRuntime) =>111const disposeExecutor = (executor: Executor) =>
106 Effect.tryPromise({112 Effect.tryPromise({
107 try: () => runtime.close(),113 try: () => executor.close(),
108 catch: (cause) =>114 catch: (cause) =>
109 cause instanceof Error ? cause : new Error(String(cause ?? "runtime close failed")),115 cause instanceof Error ? cause : new Error(String(cause ?? "executor close failed")),
110 }).pipe(Effect.orDie);116 }).pipe(Effect.orDie);
111 117
112const createRuntime = (118const createExecutorRuntime = (
113 localDataDir: string,119 localDataDir: string,
114 getLocalServerBaseUrl: () => string | undefined,120 getLocalServerBaseUrl: () => string | undefined,
115 options: StartLocalExecutorServerOptions,121 options: StartLocalExecutorServerOptions,
116) =>122) =>
117 createControlPlaneRuntime({123 createExecutorEffect({
118 workspaceRoot: options.workspaceRoot,124 backend: createLocalExecutorBackend({
125 workspaceRoot: options.workspaceRoot,
126 localDataDir,
127 }),
119 executionResolver: options.executionResolver,128 executionResolver: options.executionResolver,
120 createInternalToolMap: createExecutorAdminToolMap,129 createInternalToolMap: createWorkspaceExecutorAdminToolMap,
121 resolveSecretMaterial: options.resolveSecretMaterial,130 resolveSecretMaterial: options.resolveSecretMaterial,
122 getLocalServerBaseUrl,131 getLocalServerBaseUrl,
123 localDataDir,
124 }).pipe(132 }).pipe(
125 Effect.mapError((cause) =>133 Effect.mapError((cause) =>
126 cause instanceof Error ? cause : new Error(String(cause)),134 cause instanceof Error ? cause : new Error(String(cause)),
@@ -128,7 +136,7 @@ const createRuntime = ( · 1 unmodified line
128 );136 );
129 137
130const createControlPlaneWebHandler = (138const createControlPlaneWebHandler = (
131 runtime: ControlPlaneRuntime,139 executor: Executor,
132 tracingRuntime: ReturnType<typeof createLocalTracingRuntimeFromEnv>,140 tracingRuntime: ReturnType<typeof createLocalTracingRuntimeFromEnv>,
133) =>141) =>
134 Effect.acquireRelease(142 Effect.acquireRelease(
@@ -137,7 +145,7 @@ const createControlPlaneWebHandler = ( · 2 unmodified lines
137 Layer.merge(145 Layer.merge(
138 HttpApiBuilder.middlewareOpenApi({ path: "/v1/openapi.json" }).pipe(146 HttpApiBuilder.middlewareOpenApi({ path: "/v1/openapi.json" }).pipe(
139 Layer.provideMerge(147 Layer.provideMerge(
140 createControlPlaneApiLayer(runtime.runtimeLayer).pipe(148 createExecutorApiLayer(executor).pipe(
141 Layer.provideMerge(tracingRuntime?.layer ?? Layer.empty),149 Layer.provideMerge(tracingRuntime?.layer ?? Layer.empty),
142 ),150 ),
143 )151 )
@@ -369,14 +377,17 @@ export const createLocalExecutorRequestHandler = ( · 225 unmodified lines
369 });377 });
370 }378 }
371 379
372 const runtime = yield* Effect.acquireRelease(380 const executor = yield* Effect.acquireRelease(
373 createRuntime(requestedLocalDataDir, () => baseUrlRef, options),381 createExecutorRuntime(requestedLocalDataDir, () => baseUrlRef, options),
374 disposeRuntime,382 disposeExecutor,
375 );383 );
376 384
377 const apiHandler = yield* createControlPlaneWebHandler(runtime, tracingRuntime);385 const apiHandler = yield* createControlPlaneWebHandler(
386 executor,
387 tracingRuntime,
388 );
378 const mcpHandler = yield* Effect.acquireRelease(389 const mcpHandler = yield* Effect.acquireRelease(
379 Effect.sync(() => createExecutorMcpRequestHandler(runtime)),390 Effect.sync(() => createExecutorMcpRequestHandler(executor.runtime)),
380 (handler: ExecutorMcpHandler) =>391 (handler: ExecutorMcpHandler) =>
381 Effect.tryPromise({392 Effect.tryPromise({
382 try: () => handler.close(),393 try: () => handler.close(),
@@ -388,7 +399,8 @@ export const createLocalExecutorRequestHandler = ( · 5 unmodified lines
388 );399 );
389 400
390 return {401 return {
391 runtime,402 executor,
403 runtime: executor.runtime,
392 handleApiRequest: (request) => {404 handleApiRequest: (request) => {
393 const pathname = new URL(request.url).pathname;405 const pathname = new URL(request.url).pathname;
394 if (pathname === EXECUTOR_NPM_DIST_TAGS_PATHNAME) {406 if (pathname === EXECUTOR_NPM_DIST_TAGS_PATHNAME) {
@@ -465,6 +477,7 @@ export const createLocalExecutorServer = ( · 70 unmodified lines
465 requestHandler.setBaseUrl(baseUrl);477 requestHandler.setBaseUrl(baseUrl);
466 478
467 return {479 return {
480 executor: requestHandler.executor,
468 runtime: requestHandler.runtime,481 runtime: requestHandler.runtime,
469 host,482 host,
470 port: resolvedAddress.port,483 port: resolvedAddress.port,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t