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
4040 "@executor/platform-api": "workspace:*",
4141 "@executor/platform-internal": "workspace:*",
4242 "@executor/platform-sdk": "workspace:*",
43+ "@executor/platform-sdk-file": "workspace:*",
4344 "@executor/server": "workspace:*",
4445 "effect": "catalog:"
4546 },
apps/executor/src/cli/main.ts+18−13
@@ -13,13 +13,16 @@ import { · 12 unmodified lines
1313 createControlPlaneClient,
1414 type ControlPlaneClient,
1515 } from "@executor/platform-api";
16−import { createExecutorAdminToolMap } from "@executor/platform-internal";
16+import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
1717 import {
1818 EXECUTOR_SOURCES_ADD_HELP_LINES,
1919 RuntimeExecutionResolverService,
20− createControlPlaneRuntime,
21− type ControlPlaneRuntime,
2220 } from "@executor/platform-sdk/runtime";
21+import {
22+ createExecutorEffect,
23+ type Executor,
24+} from "@executor/platform-sdk";
25+import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
2326 import {
2427 ExecutionIdSchema,
2528 type ExecutionEnvelope,
@@ -248,9 +251,9 @@ const formatCatalogUnavailableMessage = (cause: Cause.Cause<unknown>): string => · 222 unmodified lines
248251 : `Current workspace catalog unavailable: ${message}`;
249252 };
250253
251−const closeRuntime = (runtime: ControlPlaneRuntime) =>
254+const closeExecutor = (executor: Executor) =>
252255 Effect.tryPromise({
253− try: () => runtime.close(),
256+ try: () => executor.close(),
254257 catch: toError,
255258 }).pipe(Effect.catchAll(() => Effect.void));
256259
@@ -275,27 +278,29 @@ const buildRunWorkflowText = ( · 18 unmodified lines
275278
276279 const loadRunWorkflowText = (): Effect.Effect<string, Error, never> =>
277280 Effect.acquireUseRelease(
278− createControlPlaneRuntime({
279− localDataDir: DEFAULT_LOCAL_DATA_DIR,
280− createInternalToolMap: createExecutorAdminToolMap,
281+ createExecutorEffect({
282+ backend: createLocalExecutorBackend({
283+ localDataDir: DEFAULT_LOCAL_DATA_DIR,
284+ }),
285+ createInternalToolMap: createWorkspaceExecutorAdminToolMap,
281286 }).pipe(Effect.mapError(toError)),
282− (runtime) =>
287+ (executor) =>
283288 Effect.gen(function* () {
284289 const environment = yield* Effect.gen(function* () {
285290 const resolveExecutionEnvironment = yield* RuntimeExecutionResolverService;
286291 return yield* resolveExecutionEnvironment({
287− workspaceId: runtime.localInstallation.workspaceId,
288− accountId: runtime.localInstallation.accountId,
292+ workspaceId: executor.workspaceId,
293+ accountId: executor.accountId,
289294 executionId: ExecutionIdSchema.make("exec_help"),
290295 });
291296 }).pipe(
292− Effect.provide(runtime.runtimeLayer),
297+ Effect.provide(executor.runtime.runtimeLayer),
293298 Effect.mapError(toError),
294299 );
295300
296301 return yield* buildRunWorkflowText(environment.catalog);
297302 }),
298− closeRuntime,
303+ closeExecutor,
299304 ).pipe(
300305 Effect.catchAllCause((cause) =>
301306 Effect.succeed(
packages/clients/react/package.json+1−0
@@ -24,6 +24,7 @@ · 23 unmodified lines
2424 "@effect/platform": "catalog:",
2525 "@effect/platform-node": "catalog:",
2626 "@effect/vitest": "catalog:",
27+ "@executor/platform-sdk-file": "workspace:*",
2728 "@types/jsdom": "^21.1.7",
2829 "@types/node": "catalog:",
2930 "@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
66 import {
77 createControlPlaneApiLayer,
88 } from "@executor/platform-api";
9−import {
10− createControlPlaneRuntime,
11− type ControlPlaneRuntime,
12−} from "@executor/platform-sdk/runtime";
9+import { createExecutorEffect } from "@executor/platform-sdk";
10+import type { ControlPlaneRuntime } from "@executor/platform-sdk/runtime";
11+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "@executor/platform-sdk-file";
1312 import type {
1413 LocalInstallation,
1514 Source,
@@ -110,8 +109,15 @@ const startControlPlaneServer = async (): Promise<ApiServer> => { · 94 unmodified lines
110109 const scope = await Effect.runPromise(Scope.make());
111110
112111 try {
112+ const executor = await Effect.runPromise(
113+ createExecutorEffect({
114+ backend: {
115+ createRuntime: () => Effect.succeed(runtime),
116+ },
117+ }),
118+ );
113119 const serverLayer = HttpApiBuilder.serve().pipe(
114− Layer.provide(createControlPlaneApiLayer(runtime.runtimeLayer)),
120+ Layer.provide(createControlPlaneApiLayer(executor)),
115121 Layer.provideMerge(NodeHttpServer.layerTest),
116122 );
117123 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
22 import {
33 CreateExecutionPayloadSchema,
44 ResumeExecutionPayloadSchema,
5−} from "@executor/platform-sdk/executions/contracts";
5+} from "@executor/platform-sdk/contracts";
66 export type {
77 CreateExecutionPayload,
88 ResumeExecutionPayload,
9−} from "@executor/platform-sdk/executions/contracts";
9+} from "@executor/platform-sdk/contracts";
1010 import {
1111 ExecutionIdSchema,
1212 ExecutionEnvelopeSchema,
packages/platform/api/src/executions/http.ts+4−25
@@ -1,10 +1,5 @@
11 import { HttpApiBuilder } from "@effect/platform";
22 import * as Effect from "effect/Effect";
3−import {
4− createExecution,
5− getExecution,
6− resumeExecution,
7−} from "@executor/platform-sdk/runtime";
83
94 import { ControlPlaneApi } from "../api";
105 import { resolveRequestedLocalWorkspace } from "../local-context";
@@ -16,34 +11,18 @@ export const ControlPlaneExecutionsLive = HttpApiBuilder.group( · 5 unmodified lines
1611 handlers
1712 .handle("create", ({ path, payload }) =>
1813 resolveRequestedLocalWorkspace("executions.create", path.workspaceId).pipe(
19− Effect.flatMap((runtimeLocalWorkspace) =>
20− createExecution({
21− workspaceId: path.workspaceId,
22− payload,
23− createdByAccountId: runtimeLocalWorkspace.installation.accountId,
24− })
25− ),
14+ Effect.flatMap((executor) => executor.effect.executions.create(payload)),
2615 ),
2716 )
2817 .handle("get", ({ path }) =>
2918 resolveRequestedLocalWorkspace("executions.get", path.workspaceId).pipe(
30− Effect.zipRight(
31− getExecution({
32− workspaceId: path.workspaceId,
33− executionId: path.executionId,
34− }),
35− ),
19+ Effect.flatMap((executor) => executor.effect.executions.get(path.executionId)),
3620 ),
3721 )
3822 .handle("resume", ({ path, payload }) =>
3923 resolveRequestedLocalWorkspace("executions.resume", path.workspaceId).pipe(
40− Effect.flatMap((runtimeLocalWorkspace) =>
41− resumeExecution({
42− workspaceId: path.workspaceId,
43− executionId: path.executionId,
44− payload,
45− resumedByAccountId: runtimeLocalWorkspace.installation.accountId,
46− })
24+ Effect.flatMap((executor) =>
25+ executor.effect.executions.resume(path.executionId, payload)
4726 ),
4827 ),
4928 ),
packages/platform/api/src/executor-context.ts+35−0
@@ -0,0 +1,35 @@
1+import type { Executor } from "@executor/platform-sdk";
2+import type { WorkspaceId } from "@executor/platform-sdk/schema";
3+import {
4+ ControlPlaneBadRequestError,
5+ ControlPlaneForbiddenError,
6+ ControlPlaneUnauthorizedError,
7+} from "@executor/platform-sdk/errors";
8+import * as Context from "effect/Context";
9+import * as Effect from "effect/Effect";
10+import * as Layer from "effect/Layer";
11+
12+export class ControlPlaneExecutor extends Context.Tag(
13+ "@executor/platform-api/ControlPlaneExecutor",
14+)<ControlPlaneExecutor, Executor>() {}
15+
16+export const createControlPlaneExecutorLayer = (executor: Executor) =>
17+ Layer.succeed(ControlPlaneExecutor, executor);
18+
19+export const getControlPlaneExecutor = () => ControlPlaneExecutor;
20+
21+export 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 @@
11 import { HttpApiBuilder } from "@effect/platform";
22 import * as Layer from "effect/Layer";
3+import type { Executor } from "@executor/platform-sdk";
34
45 import { ControlPlaneApi } from "./api";
6+import { createControlPlaneExecutorLayer } from "./executor-context";
57 import { ControlPlaneExecutionsLive } from "./executions/http";
68 import { ControlPlaneLocalLive } from "./local/http";
79 import { ControlPlaneOAuthLive } from "./oauth/http";
@@ -18,15 +20,14 @@ export const ControlPlaneApiLive = HttpApiBuilder.api(ControlPlaneApi).pipe( · 10 unmodified lines
1820
1921 export type ControlPlaneApiRuntimeContext = Layer.Layer.Context<typeof ControlPlaneApiLive>;
2022
21−export type BuiltControlPlaneApiLayer = Layer.Layer<
22− Layer.Layer.Success<typeof ControlPlaneApiLive>,
23− Layer.Layer.Error<typeof ControlPlaneApiLive>,
24− never
25−>;
26−
27−export const createControlPlaneApiLayer = <ERuntime>(
28− runtimeLayer: Layer.Layer<ControlPlaneApiRuntimeContext, ERuntime, never>,
29−) =>
23+export const createControlPlaneApiLayer = (executor: Executor) =>
3024 ControlPlaneApiLive.pipe(
31− Layer.provide(runtimeLayer),
25+ Layer.provide(createControlPlaneExecutorLayer(executor)),
3226 );
27+
28+export type BuiltControlPlaneApiLayer = ReturnType<
29+ typeof createControlPlaneApiLayer
30+>;
31+
32+export const createExecutorApiLayer = (executor: Executor) =>
33+ createControlPlaneApiLayer(executor);
packages/platform/api/src/index.ts+1−0
@@ -22,6 +22,7 @@ export { · 21 unmodified lines
2222 type ControlPlaneApiRuntimeContext,
2323 type BuiltControlPlaneApiLayer,
2424 createControlPlaneApiLayer,
25+ createExecutorApiLayer,
2526 } from "./http";
2627
2728 export {
packages/platform/api/src/local-context.ts+6−25
@@ -1,25 +1,6 @@
1−import type { WorkspaceId } from "@executor/platform-sdk/schema";
2−import * as Effect from "effect/Effect";
3−
4−import {
5− RuntimeLocalWorkspaceMismatchError,
6− requireRuntimeLocalWorkspace,
7−} from "@executor/platform-sdk/runtime";
8−import { ControlPlaneForbiddenError } from "./errors";
9−
10−export 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− );
1+export {
2+ createControlPlaneExecutorLayer,
3+ getControlPlaneExecutor,
4+ resolveRequestedLocalWorkspace,
5+ type ControlPlaneExecutor,
6+} from "./executor-context";
packages/platform/api/src/local/api.ts+4−3
@@ -7,7 +7,7 @@ import { · 6 unmodified lines
77 SecretListItemSchema,
88 UpdateSecretPayloadSchema,
99 UpdateSecretResultSchema,
10−} from "@executor/platform-sdk/local/contracts";
10+} from "@executor/platform-sdk/contracts";
1111 import {
1212 ControlPlaneBadRequestError,
1313 ControlPlaneNotFoundError,
@@ -26,7 +26,7 @@ export type { · 12 unmodified lines
2626 SecretProvider,
2727 UpdateSecretPayload,
2828 UpdateSecretResult,
29−} from "@executor/platform-sdk/local/contracts";
29+} from "@executor/platform-sdk/contracts";
3030
3131 export class LocalApi extends HttpApiGroup.make("local")
3232 .add(
@@ -38,7 +38,8 @@ export class LocalApi extends HttpApiGroup.make("local") · 5 unmodified lines
3838 )
3939 .add(
4040 HttpApiEndpoint.get("config")`/local/config`
41− .addSuccess(InstanceConfigSchema),
41+ .addSuccess(InstanceConfigSchema)
42+ .addError(ControlPlaneStorageError),
4243 )
4344 .add(
4445 HttpApiEndpoint.get("listSecrets")`/local/secrets`
packages/platform/api/src/local/http.ts+51−17
@@ -1,29 +1,63 @@
11 import { HttpApiBuilder } from "@effect/platform";
2−import {
3− createLocalSecret,
4− deleteLocalSecret,
5− getLocalInstanceConfig,
6− getLocalInstallation,
7− listLocalSecrets,
8− updateLocalSecret,
9−} from "@executor/platform-sdk";
2+import * as Effect from "effect/Effect";
103
114 import { ControlPlaneApi } from "../api";
5+import { ControlPlaneStorageError } from "../errors";
6+import { getControlPlaneExecutor } from "../executor-context";
7+
8+const 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+ });
1214
1315 export const ControlPlaneLocalLive = HttpApiBuilder.group(
1416 ControlPlaneApi,
1517 "local",
1618 (handlers) =>
1719 handlers
18− .handle("installation", () => getLocalInstallation())
19− .handle("config", () => getLocalInstanceConfig())
20− .handle("listSecrets", () => listLocalSecrets())
21− .handle("createSecret", ({ payload }) => createLocalSecret(payload))
20+ .handle("installation", () =>
21+ Effect.flatMap(getControlPlaneExecutor(), (executor) =>
22+ executor.effect.local.installation().pipe(
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+ )
2248 .handle("updateSecret", ({ path, payload }) =>
23− updateLocalSecret({
24− secretId: path.secretId,
25− payload,
26− }),
49+ Effect.flatMap(getControlPlaneExecutor(), (executor) =>
50+ executor.effect.secrets.update({
51+ secretId: path.secretId,
52+ payload,
53+ }).pipe(Effect.mapError(toStorageError("local.updateSecret")))
54+ )
2755 )
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+ ),
2963 );
packages/platform/api/src/oauth/http.ts+57−57
@@ -9,13 +9,15 @@ import * as Either from "effect/Either"; · 8 unmodified lines
99 import * as Effect from "effect/Effect";
1010 import * as Schema from "effect/Schema";
1111
12−import { RuntimeSourceAuthServiceTag } from "@executor/platform-sdk/runtime";
1312 import { ControlPlaneApi } from "../api";
1413 import {
1514 ControlPlaneBadRequestError,
1615 ControlPlaneStorageError,
1716 } from "../errors";
18−import { resolveRequestedLocalWorkspace } from "../local-context";
17+import {
18+ getControlPlaneExecutor,
19+ resolveRequestedLocalWorkspace,
20+} from "../local-context";
1921 import {
2022 SourceOAuthPopupResultSchema,
2123 type SourceOAuthPopupResult,
@@ -236,23 +238,20 @@ export const ControlPlaneOAuthLive = HttpApiBuilder.group( · 214 unmodified lines
236238 handlers
237239 .handle("startSourceAuth", ({ path, payload }) =>
238240 resolveRequestedLocalWorkspace("oauth.start_source_auth", path.workspaceId).pipe(
239− Effect.flatMap((runtimeLocalWorkspace) =>
241+ Effect.flatMap((executor) =>
240242 Effect.gen(function* () {
241243 const request = yield* HttpServerRequest.HttpServerRequest;
242− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
243− return yield* sourceAuthService.startSourceOAuthSession({
244− workspaceId: path.workspaceId,
245− actorAccountId: runtimeLocalWorkspace.installation.accountId,
246− baseUrl: resolveRequestOrigin(request),
247− displayName: payload.name,
248− provider: {
249− kind: payload.provider,
250− endpoint: payload.endpoint,
251− transport: payload.transport,
252− queryParams: payload.queryParams,
253− headers: payload.headers,
254− },
255− });
244+ return yield* executor.effect.oauth.startSourceAuth({
245+ baseUrl: resolveRequestOrigin(request),
246+ displayName: payload.name,
247+ provider: {
248+ kind: payload.provider,
249+ endpoint: payload.endpoint,
250+ transport: payload.transport,
251+ queryParams: payload.queryParams,
252+ headers: payload.headers,
253+ },
254+ });
256255 }).pipe(
257256 Effect.catchAllCause((cause) =>
258257 Effect.fail(
@@ -269,53 +268,54 @@ export const ControlPlaneOAuthLive = HttpApiBuilder.group( · 10 unmodified lines
269268 ),
270269 )
271270 .handle("sourceAuthCallback", ({ urlParams }) =>
272− Effect.gen(function* () {
273− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
274− const completed = yield* Effect.either(
275− sourceAuthService.completeSourceOAuthSession({
276− state: urlParams.state,
277− code: urlParams.code,
278− error: urlParams.error,
279− errorDescription: urlParams.error_description,
280− }),
281− );
271+ Effect.flatMap(getControlPlaneExecutor(), (executor) =>
272+ Effect.gen(function* () {
273+ const completed = yield* Effect.either(
274+ executor.effect.oauth.completeSourceAuth({
275+ state: urlParams.state,
276+ code: urlParams.code,
277+ error: urlParams.error,
278+ errorDescription: urlParams.error_description,
279+ }),
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";
282302
283− if (Either.isRight(completed)) {
284303 return htmlResponse(
285304 sourceOAuthPopupResultDocument({
286− title: "OAuth connected",
287− message: "OAuth credentials are ready. Return to the source form to finish saving.",
288− status: "connected",
289− sessionId: completed.right.sessionId,
305+ title: "OAuth failed",
306+ message,
307+ status: "failed",
308+ sessionId: "failed",
290309 payload: {
291310 type: "executor:oauth-result",
292− ok: true,
293− sessionId: completed.right.sessionId,
294− auth: completed.right.auth,
311+ ok: false,
312+ sessionId: null,
313+ error: message,
295314 },
296315 }),
316+ 500,
297317 );
298− }
299−
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− }),
318+ }),
319+ ),
320320 ),
321321 );
packages/platform/api/src/policies/api.ts+2−2
@@ -2,7 +2,7 @@ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform"; · 1 unmodified line
22 import {
33 CreatePolicyPayloadSchema,
44 UpdatePolicyPayloadSchema,
5−} from "@executor/platform-sdk/policies/contracts";
5+} from "@executor/platform-sdk/contracts";
66 import {
77 ControlPlaneBadRequestError,
88 ControlPlaneForbiddenError,
@@ -20,7 +20,7 @@ import * as Schema from "effect/Schema"; · 11 unmodified lines
2020 export type {
2121 CreatePolicyPayload,
2222 UpdatePolicyPayload,
23−} from "@executor/platform-sdk/policies/contracts";
23+} from "@executor/platform-sdk/contracts";
2424
2525 export {
2626 CreatePolicyPayloadSchema,
packages/platform/api/src/policies/http.ts+9−29
@@ -1,12 +1,5 @@
11 import { HttpApiBuilder } from "@effect/platform";
22 import * as Effect from "effect/Effect";
3−import {
4− createPolicy,
5− getPolicy,
6− listPolicies,
7− removePolicy,
8− updatePolicy,
9−} from "@executor/platform-sdk";
103
114 import { ControlPlaneApi } from "../api";
125 import { resolveRequestedLocalWorkspace } from "../local-context";
@@ -18,7 +11,7 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 5 unmodified lines
1811 handlers
1912 .handle("list", ({ path }) =>
2013 resolveRequestedLocalWorkspace("policies.list", path.workspaceId).pipe(
21− Effect.zipRight(listPolicies(path.workspaceId)),
14+ Effect.flatMap((executor) => executor.effect.policies.list()),
2215 ),
2316 )
2417 .handle("create", ({ path, payload }) =>
@@ -26,19 +19,12 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 1 unmodified line
2619 "policies.create",
2720 path.workspaceId,
2821 ).pipe(
29− Effect.zipRight(
30− createPolicy({ workspaceId: path.workspaceId, payload }),
31− ),
22+ Effect.flatMap((executor) => executor.effect.policies.create(payload)),
3223 ),
3324 )
3425 .handle("get", ({ path }) =>
3526 resolveRequestedLocalWorkspace("policies.get", path.workspaceId).pipe(
36− Effect.zipRight(
37− getPolicy({
38− workspaceId: path.workspaceId,
39− policyId: path.policyId,
40− }),
41− ),
27+ Effect.flatMap((executor) => executor.effect.policies.get(path.policyId)),
4228 ),
4329 )
4430 .handle("update", ({ path, payload }) =>
@@ -46,12 +32,8 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 1 unmodified line
4632 "policies.update",
4733 path.workspaceId,
4834 ).pipe(
49− Effect.zipRight(
50− updatePolicy({
51− workspaceId: path.workspaceId,
52− policyId: path.policyId,
53− payload,
54− }),
35+ Effect.flatMap((executor) =>
36+ executor.effect.policies.update(path.policyId, payload)
5537 ),
5638 ),
5739 )
@@ -60,12 +42,10 @@ export const ControlPlanePoliciesLive = HttpApiBuilder.group( · 2 unmodified lines
6042 "policies.remove",
6143 path.workspaceId,
6244 ).pipe(
63− Effect.zipRight(
64− removePolicy({
65− workspaceId: path.workspaceId,
66− policyId: path.policyId,
67− }),
68− ),
45+ Effect.flatMap((executor) => executor.effect.policies.remove(path.policyId)),
46+ Effect.map((result) => ({
47+ removed: result.removed,
48+ })),
6949 ),
7050 ),
7151 );
packages/platform/api/src/sources/api.ts+2−2
@@ -12,7 +12,7 @@ import { · 11 unmodified lines
1212 DiscoverSourcePayloadSchema,
1313 UpdateSourcePayloadSchema,
1414 WorkspaceOauthClientQuerySchema,
15−} from "@executor/platform-sdk/sources/contracts";
15+} from "@executor/platform-sdk/contracts";
1616 import {
1717 ControlPlaneBadRequestError,
1818 ControlPlaneForbiddenError,
@@ -44,7 +44,7 @@ export type { · 25 unmodified lines
4444 CreateWorkspaceOauthClientPayload,
4545 DiscoverSourcePayload,
4646 UpdateSourcePayload,
47−} from "@executor/platform-sdk/sources/contracts";
47+} from "@executor/platform-sdk/contracts";
4848
4949 export {
5050 ConnectSourceBatchPayloadSchema,
packages/platform/api/src/sources/http.ts+251−239
@@ -7,28 +7,18 @@ import type { · 6 unmodified lines
77 ExecutionInteraction,
88 Source,
99 WorkspaceId,
10+ WorkspaceOauthClient,
1011 } from "@executor/platform-sdk/schema";
12+import type {
13+ ExecutorMcpSourceInput,
14+ ExecutorSourceInput,
15+} from "@executor/platform-sdk";
1116 import * as Effect from "effect/Effect";
1217
13−import {
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";
2718 import {
2819 sourceAdapterRequiresInteractiveConnect,
29− RuntimeSourceAuthServiceTag,
30− type ExecutorAddSourceInput,
3120 } from "@executor/platform-sdk/runtime";
21+import { WorkspaceOauthClientIdSchema } from "@executor/platform-sdk/schema";
3222
3323 import {
3424 ControlPlaneBadRequestError,
@@ -37,7 +27,10 @@ import { · 2 unmodified lines
3727 } from "../errors";
3828 import { ControlPlaneApi } from "../api";
3929 import type { ConnectSourcePayload } from "./api";
40−import { resolveRequestedLocalWorkspace } from "../local-context";
30+import {
31+ getControlPlaneExecutor,
32+ resolveRequestedLocalWorkspace,
33+} from "../local-context";
4134
4235 const readHeader = (headers: unknown, name: string): string | null => {
4336 if (headers == null || typeof headers !== "object") {
@@ -93,6 +86,80 @@ const isInteractiveConnectPayload = ( · 49 unmodified lines
9386 payload.kind === undefined ||
9487 sourceAdapterRequiresInteractiveConnect(payload.kind);
9588
89+const nullToUndefined = <T>(value: T | null | undefined): T | undefined =>
90+ value ?? undefined;
91+
92+const toWorkspaceOauthClientId = (
93+ value: string | null | undefined,
94+): WorkspaceOauthClient["id"] | null | undefined =>
95+ value == null ? value : WorkspaceOauthClientIdSchema.make(value);
96+
97+const 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+
146+const 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+
96163 const escapeHtml = (value: string): string =>
97164 value
98165 .replaceAll("&", "&amp;")
@@ -595,8 +662,13 @@ const sourceOAuthPopupResultDocument = (input: { · 496 unmodified lines
595662 </body>
596663 </html>`;
597664
598−const htmlResponse = (html: string, status = 200) =>
599− HttpServerResponse.html(html).pipe(HttpServerResponse.setStatus(status));
665+const htmlResponse = (
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>;
600672
601673 const credentialErrorResponse = (input: {
602674 title: string;
@@ -636,11 +708,16 @@ const credentialSubmitErrorResponse = (input: { · 33 unmodified lines
636708 message: string;
637709 status: number;
638710 }) =>
639− getSourceCredentialInteraction({
640− workspaceId: input.workspaceId,
641− sourceId: input.sourceId,
642− interactionId: input.interactionId,
643− }).pipe(
711+ resolveRequestedLocalWorkspace(
712+ "sources.credentialSubmit",
713+ input.workspaceId,
714+ ).pipe(
715+ Effect.flatMap((executor) =>
716+ executor.effect.local.credentials.get({
717+ sourceId: input.sourceId,
718+ interactionId: input.interactionId,
719+ }),
720+ ),
644721 Effect.match({
645722 onFailure: () =>
646723 credentialErrorResponse({
@@ -671,53 +748,45 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 24 unmodified lines
671748 (handlers) =>
672749 handlers
673750 .handle("discover", ({ payload }) =>
674− discoverSource({
675− url: payload.url,
676− probeAuth: payload.probeAuth,
677− }).pipe(
678− Effect.catchAll((cause) =>
679− Effect.fail(toBadRequestError("sources.discover", cause)),
680− ),
681− ),
751+ Effect.flatMap(getControlPlaneExecutor(), (executor) =>
752+ executor.effect.sources.discover({
753+ url: payload.url,
754+ probeAuth: payload.probeAuth,
755+ }).pipe(
756+ Effect.catchAll((cause) =>
757+ Effect.fail(toBadRequestError("sources.discover", cause)),
758+ ),
759+ )
760+ )
682761 )
683762 .handle("connect", ({ path, payload }) =>
684763 resolveRequestedLocalWorkspace(
685764 "sources.connect",
686765 path.workspaceId,
687766 ).pipe(
688− Effect.flatMap((runtimeLocalWorkspace) =>
767+ Effect.flatMap((executor) =>
689768 Effect.gen(function* () {
690769 const request = yield* HttpServerRequest.HttpServerRequest;
691− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
692770 const baseUrl = resolveRequestOrigin(request);
693771
694772 if (isInteractiveConnectPayload(payload)) {
695− return yield* sourceAuthService.connectMcpSource({
696− workspaceId: path.workspaceId,
697− actorAccountId: runtimeLocalWorkspace.installation.accountId,
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− });
773+ return yield* executor.effect.sources.connect(
774+ toExecutorMcpSourceInput(payload, baseUrl),
775+ );
710776 }
711777
712− return yield* sourceAuthService.addExecutorSource(
713− {
714− workspaceId: path.workspaceId,
715− actorAccountId: runtimeLocalWorkspace.installation.accountId,
716− executionId: null,
717− interactionId: null,
718− ...(payload as Record<string, unknown>),
719− } as ExecutorAddSourceInput,
720− { baseUrl },
778+ const sourceInput = toExecutorSourceInput(payload);
779+ if (sourceInput) {
780+ return yield* executor.effect.sources.add(sourceInput, {
781+ baseUrl,
782+ });
783+ }
784+
785+ return yield* Effect.fail(
786+ toBadRequestError(
787+ "sources.connect",
788+ new Error("Unsupported source connect payload"),
789+ ),
721790 );
722791 }).pipe(
723792 Effect.catchAll((cause) =>
@@ -732,20 +801,12 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
732801 "sources.connectBatch",
733802 path.workspaceId,
734803 ).pipe(
735− Effect.flatMap((runtimeLocalWorkspace) =>
804+ Effect.flatMap((executor) =>
736805 Effect.gen(function* () {
737806 const request = yield* HttpServerRequest.HttpServerRequest;
738− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
739− const baseUrl = resolveRequestOrigin(request);
740−
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,
807+ return yield* executor.effect.sources.connectBatch({
808+ ...payload,
809+ baseUrl: resolveRequestOrigin(request),
749810 });
750811 }).pipe(
751812 Effect.catchAll((cause) =>
@@ -760,20 +821,14 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
760821 "sources.listWorkspaceOauthClients",
761822 path.workspaceId,
762823 ).pipe(
763− Effect.flatMap(() =>
764− Effect.gen(function* () {
765− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
766− return yield* sourceAuthService.listWorkspaceOauthClients({
767− workspaceId: path.workspaceId,
768− providerKey: urlParams.providerKey,
769− });
770− }).pipe(
824+ Effect.flatMap((executor) =>
825+ executor.effect.sources.oauthClients.list(urlParams.providerKey).pipe(
771826 Effect.catchAll((cause) =>
772827 Effect.fail(
773828 toBadRequestError("sources.listWorkspaceOauthClients", cause),
774829 ),
775830 ),
776− ),
831+ )
777832 ),
778833 ),
779834 )
@@ -782,16 +837,8 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
782837 "sources.createWorkspaceOauthClient",
783838 path.workspaceId,
784839 ).pipe(
785− Effect.flatMap(() =>
786− Effect.gen(function* () {
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(
840+ Effect.flatMap((executor) =>
841+ executor.effect.sources.oauthClients.create(payload).pipe(
795842 Effect.catchAll((cause) =>
796843 Effect.fail(
797844 toBadRequestError(
@@ -800,7 +847,7 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
800847 ),
801848 ),
802849 ),
803− ),
850+ )
804851 ),
805852 ),
806853 )
@@ -809,17 +856,9 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
809856 "sources.removeWorkspaceOauthClient",
810857 path.workspaceId,
811858 ).pipe(
812− Effect.flatMap(() =>
813− Effect.gen(function* () {
814− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
815− const removed =
816− yield* sourceAuthService.removeWorkspaceOauthClient({
817− workspaceId: path.workspaceId,
818− oauthClientId: path.oauthClientId,
819− });
820−
821− return { removed };
822− }).pipe(
859+ Effect.flatMap((executor) =>
860+ executor.effect.sources.oauthClients.remove(path.oauthClientId).pipe(
861+ Effect.map((result) => ({ removed: result })),
823862 Effect.catchAll((cause) =>
824863 Effect.fail(
825864 toBadRequestError(
@@ -828,7 +867,7 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
828867 ),
829868 ),
830869 ),
831− ),
870+ )
832871 ),
833872 ),
834873 )
@@ -837,60 +876,34 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
837876 "sources.removeProviderAuthGrant",
838877 path.workspaceId,
839878 ).pipe(
840− Effect.flatMap(() =>
841− Effect.gen(function* () {
842− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
843− const removed = yield* sourceAuthService
844− .removeProviderAuthGrant({
845− workspaceId: path.workspaceId,
846− grantId: path.grantId,
847− })
848− .pipe(
849− Effect.catchAll((cause) =>
850− Effect.fail(
851− toBadRequestError(
852− "sources.removeProviderAuthGrant",
853− cause,
854− ),
855− ),
879+ Effect.flatMap((executor) =>
880+ executor.effect.sources.providerGrants.remove(path.grantId).pipe(
881+ Effect.map((result) => ({ removed: result })),
882+ Effect.catchAll((cause) =>
883+ Effect.fail(
884+ toBadRequestError(
885+ "sources.removeProviderAuthGrant",
886+ cause,
856887 ),
857− );
858−
859− return { removed };
860− }),
888+ ),
889+ ),
890+ )
861891 ),
862892 ),
863893 )
864894 .handle("list", ({ path }) =>
865895 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(
866− Effect.flatMap((runtimeLocalWorkspace) =>
867− listSources({
868− workspaceId: path.workspaceId,
869− accountId: runtimeLocalWorkspace.installation.accountId,
870− }),
871− ),
896+ Effect.flatMap((executor) => executor.effect.sources.list()),
872897 ),
873898 )
874899 .handle("create", ({ path, payload }) =>
875900 resolveRequestedLocalWorkspace("sources.create", path.workspaceId).pipe(
876− Effect.flatMap((runtimeLocalWorkspace) =>
877− createSource({
878− workspaceId: path.workspaceId,
879− accountId: runtimeLocalWorkspace.installation.accountId,
880− payload,
881− }),
882− ),
901+ Effect.flatMap((executor) => executor.effect.sources.create(payload)),
883902 ),
884903 )
885904 .handle("get", ({ path }) =>
886905 resolveRequestedLocalWorkspace("sources.get", path.workspaceId).pipe(
887− Effect.flatMap((runtimeLocalWorkspace) =>
888− getSource({
889− workspaceId: path.workspaceId,
890− sourceId: path.sourceId,
891− accountId: runtimeLocalWorkspace.installation.accountId,
892− }),
893− ),
906+ Effect.flatMap((executor) => executor.effect.sources.get(path.sourceId)),
894907 ),
895908 )
896909 .handle("inspection", ({ path }) =>
@@ -898,11 +911,8 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 1 unmodified line
898911 "sources.inspection",
899912 path.workspaceId,
900913 ).pipe(
901− Effect.zipRight(
902− getSourceInspection({
903− workspaceId: path.workspaceId,
904− sourceId: path.sourceId,
905− }),
914+ Effect.flatMap((executor) =>
915+ executor.effect.sources.inspection.get(path.sourceId),
906916 ),
907917 ),
908918 )
@@ -911,12 +921,11 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
911921 "sources.inspection_tool",
912922 path.workspaceId,
913923 ).pipe(
914− Effect.zipRight(
915− getSourceInspectionToolDetail({
916− workspaceId: path.workspaceId,
917− sourceId: path.sourceId,
918− toolPath: path.toolPath,
919− }),
924+ Effect.flatMap((executor) =>
925+ executor.effect.sources.inspection.tool({
926+ sourceId: path.sourceId,
927+ toolPath: path.toolPath,
928+ }),
920929 ),
921930 ),
922931 )
@@ -925,43 +934,38 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
925934 "sources.inspection_discover",
926935 path.workspaceId,
927936 ).pipe(
928− Effect.zipRight(
929− discoverSourceInspectionTools({
930− workspaceId: path.workspaceId,
931− sourceId: path.sourceId,
932− payload,
933− }),
937+ Effect.flatMap((executor) =>
938+ executor.effect.sources.inspection.discover({
939+ sourceId: path.sourceId,
940+ payload,
941+ }),
934942 ),
935943 ),
936944 )
937945 .handle("update", ({ path, payload }) =>
938946 resolveRequestedLocalWorkspace("sources.update", path.workspaceId).pipe(
939− Effect.flatMap((runtimeLocalWorkspace) =>
940− updateSource({
941− workspaceId: path.workspaceId,
942− sourceId: path.sourceId,
943− accountId: runtimeLocalWorkspace.installation.accountId,
944− payload,
945− }),
947+ Effect.flatMap((executor) =>
948+ executor.effect.sources.update(path.sourceId, payload)
946949 ),
947950 ),
948951 )
949952 .handle("remove", ({ path }) =>
950953 resolveRequestedLocalWorkspace("sources.remove", path.workspaceId).pipe(
951− Effect.zipRight(
952− removeSource({
953− workspaceId: path.workspaceId,
954− sourceId: path.sourceId,
955− }),
956− ),
954+ Effect.flatMap((executor) => executor.effect.sources.remove(path.sourceId)),
955+ Effect.map((result) => ({ removed: result.removed })),
957956 ),
958957 )
959958 .handle("credentialPage", ({ path, urlParams }) =>
960− getSourceCredentialInteraction({
961− workspaceId: path.workspaceId,
962− sourceId: path.sourceId,
963− interactionId: urlParams.interactionId,
964− }).pipe(
959+ resolveRequestedLocalWorkspace(
960+ "sources.credentialPage",
961+ path.workspaceId,
962+ ).pipe(
963+ Effect.flatMap((executor) =>
964+ executor.effect.local.credentials.get({
965+ sourceId: path.sourceId,
966+ interactionId: urlParams.interactionId,
967+ }),
968+ ),
965969 Effect.map((interaction) =>
966970 htmlResponse(
967971 credentialPageDocument({
@@ -976,39 +980,44 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 8 unmodified lines
976980 }),
977981 ),
978982 ),
979− Effect.catchTags({
980− ControlPlaneNotFoundError: (error) =>
981− Effect.succeed(
982− credentialErrorResponse({
983− title: "Source Credential Request Unavailable",
984− message: error.message,
985− status: credentialErrorStatus(error),
986− }),
987− ),
988− ControlPlaneStorageError: (error) =>
989− Effect.succeed(
990− credentialErrorResponse({
991− title: "Source Credential Request Failed",
992− message: error.message,
993− status: credentialErrorStatus(error),
994− }),
995− ),
996− }),
983+ Effect.catchAll((error) =>
984+ Effect.succeed(
985+ credentialErrorResponse({
986+ title:
987+ error instanceof ControlPlaneNotFoundError
988+ ? "Source Credential Request Unavailable"
989+ : "Source Credential Request Failed",
990+ message:
991+ error instanceof Error ? error.message : "Source credential request failed",
992+ status:
993+ error instanceof ControlPlaneNotFoundError
994+ || error instanceof ControlPlaneStorageError
995+ || error instanceof ControlPlaneBadRequestError
996+ ? credentialErrorStatus(error)
997+ : 500,
998+ }),
999+ ),
1000+ ),
9971001 ),
9981002 )
9991003 .handle("credentialSubmit", ({ path, urlParams, payload }) =>
1000− submitSourceCredentialInteraction({
1001− workspaceId: path.workspaceId,
1002− sourceId: path.sourceId,
1003− interactionId: urlParams.interactionId,
1004− action:
1005− payload.action === "cancel"
1006− ? "cancel"
1007− : payload.action === "continue"
1008− ? "continue"
1009− : "submit",
1010− token: payload.token,
1011− }).pipe(
1004+ resolveRequestedLocalWorkspace(
1005+ "sources.credentialSubmit",
1006+ path.workspaceId,
1007+ ).pipe(
1008+ Effect.flatMap((executor) =>
1009+ executor.effect.local.credentials.submit({
1010+ sourceId: path.sourceId,
1011+ interactionId: urlParams.interactionId,
1012+ action:
1013+ payload.action === "cancel"
1014+ ? "cancel"
1015+ : payload.action === "continue"
1016+ ? "continue"
1017+ : "submit",
1018+ token: payload.token,
1019+ }),
1020+ ),
10121021 Effect.map((result) =>
10131022 htmlResponse(
10141023 credentialPageDocument({
@@ -1037,43 +1046,49 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 22 unmodified lines
10371046 }),
10381047 ),
10391048 ),
1040− Effect.catchTags({
1041− ControlPlaneBadRequestError: (error) =>
1042− credentialSubmitErrorResponse({
1049+ Effect.catchAll((error) => {
1050+ if (error instanceof ControlPlaneBadRequestError) {
1051+ return credentialSubmitErrorResponse({
10431052 workspaceId: path.workspaceId,
10441053 sourceId: path.sourceId,
10451054 interactionId: urlParams.interactionId,
10461055 message: error.message,
10471056 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,
10481073 }),
1049− ControlPlaneNotFoundError: (error) =>
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− ),
1074+ );
10651075 }),
10661076 ),
10671077 )
10681078 .handle("credentialComplete", ({ path, urlParams }) =>
1069− completeSourceCredentialSetup({
1070− workspaceId: path.workspaceId,
1071− sourceId: path.sourceId,
1072− state: urlParams.state,
1073− code: urlParams.code,
1074− error: urlParams.error,
1075− errorDescription: urlParams.error_description,
1076− }).pipe(
1079+ resolveRequestedLocalWorkspace(
1080+ "sources.credentialComplete",
1081+ path.workspaceId,
1082+ ).pipe(
1083+ Effect.flatMap((executor) =>
1084+ executor.effect.local.credentials.complete({
1085+ sourceId: path.sourceId,
1086+ state: urlParams.state,
1087+ code: urlParams.code,
1088+ error: urlParams.error,
1089+ errorDescription: urlParams.error_description,
1090+ }),
1091+ ),
10771092 Effect.map((completed) =>
10781093 htmlResponse(
10791094 sourceOAuthPopupResultDocument({
@@ -1119,13 +1134,10 @@ export const ControlPlaneSourcesLive = HttpApiBuilder.group( · 39 unmodified lines
11191134 "sources.providerOauthComplete",
11201135 path.workspaceId,
11211136 ).pipe(
1122− Effect.flatMap((runtimeLocalWorkspace) =>
1137+ Effect.flatMap((executor) =>
11231138 Effect.gen(function* () {
1124− const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
1125− const completed =
1126− yield* sourceAuthService.completeProviderOauthCallback({
1139+ const completed = yield* executor.effect.oauth.completeProviderCallback({
11271140 workspaceId: path.workspaceId,
1128− actorAccountId: runtimeLocalWorkspace.installation.accountId,
11291141 state: urlParams.state,
11301142 code: urlParams.code,
11311143 error: urlParams.error,
packages/platform/internal/src/tools/executor-admin.ts+309−26
@@ -1,4 +1,5 @@
11 import { toTool, type ToolMap } from "@executor/codemode-core";
2+import type { Executor } from "@executor/platform-sdk";
23 import {
34 type AccountId,
45 LocalInstallationSchema,
@@ -45,47 +46,39 @@ import { · 40 unmodified lines
4546 UpdateSecretPayloadSchema,
4647 UpdateSecretResultSchema,
4748 type UpdateSecretPayload,
48−} from "@executor/platform-sdk/local/contracts";
49+} from "@executor/platform-sdk/contracts";
4950 import {
5051 createLocalSecret,
52+ createPolicy,
5153 deleteLocalSecret,
54+ discoverSource,
55+ discoverSourceInspectionTools,
5256 getLocalInstanceConfig,
57+ getPolicy,
58+ getSource,
59+ getSourceInspection,
60+ getSourceInspectionToolDetail,
5361 listLocalSecrets,
62+ listPolicies,
63+ listSources,
64+ removePolicy,
65+ removeSource,
5466 updateLocalSecret,
55−} from "@executor/platform-sdk/local/secrets";
67+ updatePolicy,
68+ updateSource,
69+} from "@executor/platform-sdk/operations";
5670 import {
5771 CreatePolicyPayloadSchema,
5872 type CreatePolicyPayload,
5973 type UpdatePolicyPayload,
6074 UpdatePolicyPayloadSchema,
61−} from "@executor/platform-sdk/policies/contracts";
62−import {
63− createPolicy,
64− getPolicy,
65− listPolicies,
66− removePolicy,
67− updatePolicy,
68−} from "@executor/platform-sdk/policies/operations";
75+} from "@executor/platform-sdk/contracts";
6976 import {
7077 CreateWorkspaceOauthClientPayloadSchema,
7178 DiscoverSourcePayloadSchema,
7279 type CreateWorkspaceOauthClientPayload,
7380 UpdateSourcePayloadSchema,
74−} from "@executor/platform-sdk/sources/contracts";
75−import {
76− discoverSource,
77−} from "@executor/platform-sdk/sources/discovery";
78−import {
79− discoverSourceInspectionTools,
80− getSourceInspection,
81− getSourceInspectionToolDetail,
82−} from "@executor/platform-sdk/sources/inspection";
83−import {
84− getSource,
85− listSources,
86− removeSource,
87− updateSource,
88−} from "@executor/platform-sdk/sources/operations";
81+} from "@executor/platform-sdk/contracts";
8982
9083 const emptyInputSchema = Schema.standardSchemaV1(Schema.Struct({}));
9184 const localInstallationOutputSchema = Schema.standardSchemaV1(
@@ -260,7 +253,7 @@ const runWorkspaceStorageEffect = <A, E, R>(input: { · 168 unmodified lines
260253 ) as Effect.Effect<A, E, never>,
261254 );
262255
263−export const createExecutorAdminToolMap = (
256+export const createWorkspaceExecutorAdminToolMap = (
264257 input: WorkspaceInternalToolContext,
265258 ): ToolMap => {
266259 const runtimeLayer = makeRuntimeLayer({
@@ -690,3 +683,293 @@ export const createExecutorAdminToolMap = ( · 423 unmodified lines
690683 }),
691684 };
692685 };
686+
687+export 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
44 "type": "module",
55 "exports": {
66 ".": "./src/index.ts",
7+ "./backend": "./src/backend.ts",
8+ "./contracts": "./src/contracts.ts",
9+ "./executor": "./src/executor.ts",
710 "./errors": "./src/errors.ts",
11+ "./operations": "./src/operations.ts",
812 "./string-schemas": "./src/string-schemas.ts",
9− "./executions/contracts": "./src/executions/contracts.ts",
1013 "./schema": "./src/schema/index.ts",
11− "./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"
14+ "./runtime": "./src/runtime/index.ts"
2115 },
2216 "imports": {
2317 "#runtime": "./src/runtime/index.ts",
packages/platform/sdk/src/backend.ts+42−0
@@ -0,0 +1,42 @@
1+import * as Effect from "effect/Effect";
2+
3+import {
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+
16+export type ExecutorBackend = {
17+ createRuntime: (
18+ options: RuntimeControlPlaneOptions,
19+ ) => Effect.Effect<ControlPlaneRuntime, Error>;
20+};
21+
22+export type ExecutorBackendServices = RuntimeControlPlaneServices;
23+export type ExecutorInstallationBackend = BoundInstallationStore;
24+export type ExecutorWorkspaceConfigBackend = BoundWorkspaceConfigStore;
25+export type ExecutorWorkspaceStateBackend = BoundWorkspaceStateStore;
26+export type ExecutorSourceArtifactBackend = BoundSourceArtifactStore;
27+export type ExecutorLocalToolBackend = BoundLocalToolRuntimeLoader;
28+export type ExecutorSecretMaterialBackend = RuntimeSecretMaterialServices;
29+
30+export 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 @@
1+export * from "./executions/contracts";
2+export * from "./local/contracts";
3+export * from "./policies/contracts";
4+export * from "./sources/contracts";
packages/platform/sdk/src/executor.ts+694−0
@@ -0,0 +1,694 @@
1+import * as Effect from "effect/Effect";
2+
3+import type {
4+ AccountId,
5+ Execution,
6+ ExecutionEnvelope,
7+ ExecutionInteraction,
8+ LocalInstallation,
9+ LocalWorkspacePolicy,
10+ ProviderAuthGrant,
11+ Source,
12+ WorkspaceId,
13+ WorkspaceOauthClient,
14+} from "./schema";
15+import { ExecutionIdSchema } from "./schema";
16+import type { CreateExecutionPayload, ResumeExecutionPayload } from "./executions/contracts";
17+import type {
18+ CreateSecretPayload,
19+ CreateSecretResult,
20+ DeleteSecretResult,
21+ InstanceConfig,
22+ SecretListItem,
23+ UpdateSecretPayload,
24+ UpdateSecretResult,
25+} from "./local/contracts";
26+import {
27+ completeSourceCredentialSetup,
28+ getLocalInstallation,
29+ getSourceCredentialInteraction,
30+ submitSourceCredentialInteraction,
31+} from "./local/operations";
32+import {
33+ createLocalSecret,
34+ deleteLocalSecret,
35+ getLocalInstanceConfig,
36+ listLocalSecrets,
37+ updateLocalSecret,
38+} from "./local/secrets";
39+import type { CreatePolicyPayload, UpdatePolicyPayload } from "./policies/contracts";
40+import {
41+ createPolicy,
42+ getPolicy,
43+ listPolicies,
44+ removePolicy,
45+ updatePolicy,
46+} from "./policies/operations";
47+import type {
48+ CreateSourcePayload,
49+ CreateWorkspaceOauthClientPayload,
50+ UpdateSourcePayload,
51+} from "./sources/contracts";
52+import { discoverSource } from "./sources/discovery";
53+import {
54+ discoverSourceInspectionTools,
55+ getSourceInspection,
56+ getSourceInspectionToolDetail,
57+} from "./sources/inspection";
58+import {
59+ createSource,
60+ getSource,
61+ listSources,
62+ removeSource,
63+ updateSource,
64+} from "./sources/operations";
65+import type { ExecutorBackend } from "./backend";
66+import {
67+ provideControlPlaneRuntime,
68+ type ControlPlaneRuntime,
69+ type CreateWorkspaceInternalToolMap,
70+ type ResolveExecutionEnvironment,
71+ type ResolveSecretMaterial,
72+ RuntimeSourceAuthServiceTag,
73+} from "./runtime";
74+import { createExecution, getExecution, resumeExecution } from "./runtime/execution/service";
75+import 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+
89+export type ExecutorRuntimeOptions = {
90+ executionResolver?: ResolveExecutionEnvironment;
91+ createInternalToolMap?: CreateWorkspaceInternalToolMap;
92+ resolveSecretMaterial?: ResolveSecretMaterial;
93+ getLocalServerBaseUrl?: () => string | undefined;
94+};
95+
96+type DistributiveOmit<T, Keys extends PropertyKey> = T extends unknown ? Omit<T, Keys> : never;
97+type ProvidedEffect<T extends Effect.Effect<any, any, any>> = Effect.Effect<
98+ Effect.Effect.Success<T>,
99+ Effect.Effect.Error<T>,
100+ never
101+>;
102+
103+export type ExecutorSourceInput = DistributiveOmit<
104+ ExecutorAddSourceInput,
105+ "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
106+>;
107+
108+export type ExecutorSourceBatchInput = DistributiveOmit<
109+ ConnectGoogleDiscoveryBatchInput,
110+ "workspaceId" | "actorAccountId" | "executionId" | "interactionId"
111+>;
112+
113+export type ExecutorMcpSourceInput = DistributiveOmit<
114+ ConnectMcpSourceInput,
115+ "workspaceId" | "actorAccountId"
116+>;
117+
118+export type ExecutorSourceOAuthInput = DistributiveOmit<
119+ StartSourceOAuthSessionInput,
120+ "workspaceId" | "actorAccountId"
121+>;
122+
123+export 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+
360+export type CreateExecutorOptions = ExecutorRuntimeOptions & {
361+ backend: ExecutorBackend;
362+};
363+
364+const 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+
679+export 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+
692+export 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 @@
1−export * from "./errors";
2−export * from "./string-schemas";
3−export * from "./executions/contracts";
4−export * from "./local/contracts";
5−export * from "./local/operations";
6−export * from "./local/secrets";
7−export * from "./policies/contracts";
8−export * from "./policies/operations";
9−export * from "./sources/contracts";
10−export * from "./sources/discovery";
11−export * from "./sources/inspection";
12−export * from "./sources/operations";
1+export * from "./backend";
2+export * from "./executor";
packages/platform/sdk/src/operations.ts+6−0
@@ -0,0 +1,6 @@
1+export * from "./local/operations";
2+export * from "./local/secrets";
3+export * from "./policies/operations";
4+export * from "./sources/discovery";
5+export * from "./sources/inspection";
6+export * from "./sources/operations";
packages/platform/sdk/src/runtime/control-plane-runtime.test.ts+3−3
@@ -23,15 +23,15 @@ import { · 22 unmodified lines
2323 import type { ToolPath } from "@executor/codemode-core";
2424 import { createCatalogImportMetadata } from "@executor/source-core";
2525 import { createGraphqlCatalogFragment } from "@executor/source-graphql";
26−import { createExecutorAdminToolMap } from "../../../internal/src/index";
26+import { createWorkspaceExecutorAdminToolMap } from "../../../internal/src/index";
2727
2828 import {
2929 type ControlPlaneRuntime,
30− createControlPlaneRuntime,
3130 LiveExecutionManagerService,
3231 provideControlPlaneRuntime,
3332 RuntimeExecutionResolverService,
3433 } from "./index";
34+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../sdk-file/src/index";
3535 import { createSourceFromPayload } from "./sources/source-definitions";
3636 import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions";
3737 import { persistSource } from "./sources/source-store";
@@ -67,7 +67,7 @@ const makeRuntime = Effect.gen(function* () { · 29 unmodified lines
6767 workspaceRoot,
6868 homeConfigPath,
6969 homeStateDirectory,
70− createInternalToolMap: createExecutorAdminToolMap,
70+ createInternalToolMap: createWorkspaceExecutorAdminToolMap,
7171 }),
7272 (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie),
7373 );
packages/platform/sdk/src/runtime/execution/http.test.ts+1−3
@@ -9,9 +9,7 @@ import * as Schema from "effect/Schema"; · 8 unmodified lines
99 import { makeToolInvokerFromTools } from "@executor/codemode-core";
1010 import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
1111
12−import {
13− createControlPlaneRuntime,
14−} from "../index";
12+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
1513 import { withControlPlaneClient } from "./test-http-client";
1614
1715 const 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
2020 import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess";
2121
2222 import {
23− createControlPlaneRuntime,
2423 type ResolveExecutionEnvironment,
2524 } from "../index";
25+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
2626 import { withControlPlaneClient } from "./test-http-client";
2727
2828 type 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
55 import { describe, expect, it } from "@effect/vitest";
66 import * as Effect from "effect/Effect";
77
8−import {
9− createControlPlaneRuntime,
10−} from "../index";
8+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
119 import { withControlPlaneClient } from "./test-http-client";
1210
1311 const 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
1111 import * as Layer from "effect/Layer";
1212
1313 import {
14+ createExecutorEffect,
1415 type ControlPlaneRuntime,
15−} from "../index";
16+} from "../../index";
1617
1718 const 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+ );
1929
2030 return HttpApiBuilder.serve().pipe(
2131 Layer.provide(apiLayer),
packages/platform/sdk/src/runtime/execution/workspace/environment.ts+6−0
@@ -26,6 +26,8 @@ import { · 25 unmodified lines
2626 type LocalToolRuntime,
2727 } from "../../local/tools";
2828 import {
29+ InstallationStore,
30+ type InstallationStoreShape,
2931 SourceArtifactStore,
3032 type SourceArtifactStoreShape,
3133 WorkspaceConfigStore,
@@ -65,6 +67,7 @@ export const createWorkspaceExecutionEnvironmentResolver = · 33 unmodified lines
6567 typeof RuntimeSourceCatalogStoreService
6668 >;
6769 localToolRuntimeLoader: LocalToolRuntimeLoaderShape;
70+ installationStore: InstallationStoreShape;
6871 workspaceConfigStore: WorkspaceConfigStoreShape;
6972 workspaceStateStore: WorkspaceStateStoreShape;
7073 sourceArtifactStore: SourceArtifactStoreShape;
@@ -92,6 +95,7 @@ export const createWorkspaceExecutionEnvironmentResolver = · 21 unmodified lines
9295 sourceStore: input.sourceStore,
9396 sourceCatalogSyncService: input.sourceCatalogSyncService,
9497 sourceCatalogStore: input.sourceCatalogStore,
98+ installationStore: input.installationStore,
9599 workspaceConfigStore: input.workspaceConfigStore,
96100 workspaceStateStore: input.workspaceStateStore,
97101 sourceArtifactStore: input.sourceArtifactStore,
@@ -141,6 +145,7 @@ export const RuntimeExecutionResolverLive = ( · 43 unmodified lines
141145 const sourceAuthService = yield* RuntimeSourceAuthServiceTag;
142146 const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService;
143147 const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService;
148+ const installationStore = yield* InstallationStore;
144149 const workspaceConfigStore = yield* WorkspaceConfigStore;
145150 const workspaceStateStore = yield* WorkspaceStateStore;
146151 const sourceArtifactStore = yield* SourceArtifactStore;
@@ -153,6 +158,7 @@ export const RuntimeExecutionResolverLive = ( · 6 unmodified lines
153158 sourceAuthMaterialService,
154159 sourceCatalogStore,
155160 localToolRuntimeLoader,
161+ installationStore,
156162 workspaceConfigStore,
157163 workspaceStateStore,
158164 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
1616 import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context";
1717 import { type LocalToolRuntime } from "../../local/tools";
1818 import {
19+ type InstallationStoreShape,
1920 makeWorkspaceStorageLayer,
2021 type SourceArtifactStoreShape,
2122 type WorkspaceConfigStoreShape,
@@ -47,6 +48,7 @@ export type WorkspaceInternalToolContext = { · 25 unmodified lines
4748 typeof RuntimeSourceCatalogSyncService
4849 >;
4950 sourceAuthService: RuntimeSourceAuthService;
51+ installationStore: InstallationStoreShape;
5052 workspaceConfigStore: WorkspaceConfigStoreShape;
5153 workspaceStateStore: WorkspaceStateStoreShape;
5254 sourceArtifactStore: SourceArtifactStoreShape;
@@ -68,6 +70,7 @@ export const createWorkspaceToolInvoker = (input: { · 15 unmodified lines
6870 sourceCatalogStore: Effect.Effect.Success<
6971 typeof RuntimeSourceCatalogStoreService
7072 >;
73+ installationStore: InstallationStoreShape;
7174 workspaceConfigStore: WorkspaceConfigStoreShape;
7275 workspaceStateStore: WorkspaceStateStoreShape;
7376 sourceArtifactStore: SourceArtifactStoreShape;
@@ -97,6 +100,10 @@ export const createWorkspaceToolInvoker = (input: { · 23 unmodified lines
97100 workspaceId: input.workspaceId,
98101 accountId: input.accountId,
99102 sourceAuthService: input.sourceAuthService,
103+ installationStore: input.installationStore,
104+ workspaceConfigStore: input.workspaceConfigStore,
105+ workspaceStateStore: input.workspaceStateStore,
106+ sourceArtifactStore: input.sourceArtifactStore,
100107 runtimeLocalWorkspace: input.runtimeLocalWorkspace,
101108 });
102109 const internalTools =
@@ -107,6 +114,7 @@ export const createWorkspaceToolInvoker = (input: { · 4 unmodified lines
107114 sourceStore: input.sourceStore,
108115 sourceCatalogSyncService: input.sourceCatalogSyncService,
109116 sourceAuthService: input.sourceAuthService,
117+ installationStore: input.installationStore,
110118 workspaceConfigStore: input.workspaceConfigStore,
111119 workspaceStateStore: input.workspaceStateStore,
112120 sourceArtifactStore: input.sourceArtifactStore,
packages/platform/sdk/src/runtime/index.ts+207−113
@@ -1,51 +1,66 @@
1−import { FileSystem } from "@effect/platform";
1+import {
2+ createToolCatalogFromTools,
3+ makeToolInvokerFromTools,
4+ type ToolMap,
5+} from "@executor/codemode-core";
6+import { clearAllMcpConnectionPools } from "@executor/source-mcp";
7+import type { LocalInstallation } from "#schema";
28 import * as Effect from "effect/Effect";
3−import * as Exit from "effect/Exit";
49 import * as Layer from "effect/Layer";
510 import * as ManagedRuntime from "effect/ManagedRuntime";
6−import * as Scope from "effect/Scope";
7−import { NodeFileSystem } from "@effect/platform-node";
8−import { clearAllMcpConnectionPools } from "@executor/source-mcp";
9−import type { LocalInstallation } from "#schema";
1011
11−import { type ResolveExecutionEnvironment } from "./execution/state";
12−import {
13− createLiveExecutionManager,
14− LiveExecutionManagerService,
15−} from "./execution/live";
16−import {
17− createLocalControlPlanePersistence,
18− type LocalControlPlanePersistence,
19−} from "./local/control-plane-store";
20−
21−import { resolveLocalWorkspaceContext } from "./local/config";
12+import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material";
13+import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime";
14+import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile";
15+import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync";
16+import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live";
17+import { RuntimeExecutionResolverLive } from "./execution/workspace/environment";
18+import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker";
19+import type {
20+ LoadedLocalExecutorConfig,
21+ ResolvedLocalWorkspaceContext,
22+} from "./local/config";
23+import type { LocalExecutorConfig } from "#schema";
24+import type { LocalControlPlanePersistence } from "./local/control-plane-store";
2225 import {
23− LocalStorageLive,
24− LocalInstallationStore,
25− LocalWorkspaceConfigStore,
26−} from "./local/storage";
26+ type DeleteSecretMaterial,
27+ type ResolveSecretMaterial,
28+ SecretMaterialDeleterService,
29+ SecretMaterialResolverService,
30+ SecretMaterialStorerService,
31+ SecretMaterialUpdaterService,
32+ type StoreSecretMaterial,
33+ type UpdateSecretMaterial,
34+} from "./local/secret-material-providers";
35+import type { LocalSourceArtifact } from "./local/source-artifacts";
2736 import {
2837 type RuntimeLocalWorkspaceState,
2938 RuntimeLocalWorkspaceLive,
3039 } from "./local/runtime-context";
31−import { LocalToolRuntimeLoaderLive } from "./local/tools";
40+import {
41+ type LocalToolRuntime,
42+ type LocalToolRuntimeLoaderShape,
43+ LocalToolRuntimeLoaderService,
44+} from "./local/tools";
45+import {
46+ InstallationStore,
47+ makeLocalStorageLayer,
48+ type InstallationStoreShape,
49+ type SourceArtifactStoreShape,
50+ type WorkspaceConfigStoreShape,
51+ type WorkspaceStateStoreShape,
52+} from "./local/storage";
53+import type { LocalWorkspaceState } from "./local/workspace-state";
3254 import { synchronizeLocalWorkspaceState } from "./local/workspace-sync";
3355 import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store";
34−import { RuntimeSourceStoreLive } from "./sources/source-store";
35−import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime";
36−import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile";
37−import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material";
38−import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync";
3956 import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service";
40−import type { ResolveSecretMaterial } from "./local/secret-material-providers";
41−import { SecretMaterialLive } from "./local/secret-material-providers";
42−import { RuntimeExecutionResolverLive } from "./execution/workspace/environment";
43−import type { CreateWorkspaceInternalToolMap } from "./execution/workspace/tool-invoker";
57+import { RuntimeSourceStoreLive } from "./sources/source-store";
4458
4559 export * from "./execution/state";
4660 export * from "./sources/executor-tools";
4761 export * from "./execution/live";
4862 export * from "./local/config";
63+export * from "./local/control-plane-store";
4964 export * from "./local/errors";
5065 export * from "./local/installation";
5166 export * from "./local/runtime-context";
@@ -89,54 +104,157 @@ export type RuntimeControlPlaneOptions = { · 37 unmodified lines
89104 createInternalToolMap?: CreateWorkspaceInternalToolMap;
90105 resolveSecretMaterial?: ResolveSecretMaterial;
91106 getLocalServerBaseUrl?: () => string | undefined;
92− localDataDir?: string;
93− workspaceRoot?: string;
94− homeConfigPath?: string;
95− homeStateDirectory?: string;
96107 };
97108
109+type ResolveExecutionEnvironment = import("./execution/state").ResolveExecutionEnvironment;
110+
98111 const detailsFromCause = (cause: unknown): string =>
99112 cause instanceof Error ? cause.message : String(cause);
100113
101−const toLocalRuntimeBootstrapError = (cause: unknown): Error => {
114+const toRuntimeBootstrapError = (cause: unknown): Error => {
102115 const details = detailsFromCause(cause);
103− return new Error(`Failed initializing local runtime: ${details}`);
116+ return new Error(`Failed initializing runtime: ${details}`);
104117 };
105118
106−const closeScope = (scope: Scope.CloseableScope) =>
107− Scope.close(scope, Exit.void).pipe(Effect.orDie);
108−
109119 export type RuntimeControlPlaneLayer = Layer.Layer<any, never, never>;
110120
121+export type BoundInstallationStore = {
122+ load: () => Effect.Effect<LocalInstallation, Error, never>;
123+ getOrProvision: () => Effect.Effect<LocalInstallation, Error, never>;
124+};
125+
126+export 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+
134+export type BoundWorkspaceStateStore = {
135+ load: () => Effect.Effect<LocalWorkspaceState, Error, never>;
136+ write: (
137+ state: LocalWorkspaceState,
138+ ) => Effect.Effect<void, Error, never>;
139+};
140+
141+export 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+
153+export type BoundLocalToolRuntimeLoader = {
154+ load: () => ReturnType<LocalToolRuntimeLoaderShape["load"]>;
155+};
156+
157+export type RuntimeSecretMaterialServices = {
158+ resolve: ResolveSecretMaterial;
159+ store: StoreSecretMaterial;
160+ delete: DeleteSecretMaterial;
161+ update: UpdateSecretMaterial;
162+};
163+
164+export 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+
175+const 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+
185+const toInstallationStoreShape = (
186+ input: BoundInstallationStore,
187+): InstallationStoreShape => ({
188+ load: () => input.load(),
189+ getOrProvision: () => input.getOrProvision(),
190+});
191+
192+const toWorkspaceConfigStoreShape = (
193+ input: BoundWorkspaceConfigStore,
194+): WorkspaceConfigStoreShape => ({
195+ load: () => input.load(),
196+ writeProject: ({ config }) => input.writeProject(config),
197+ resolveRelativePath: input.resolveRelativePath,
198+});
199+
200+const toWorkspaceStateStoreShape = (
201+ input: BoundWorkspaceStateStore,
202+): WorkspaceStateStoreShape => ({
203+ load: () => input.load(),
204+ write: ({ state }) => input.write(state),
205+});
206+
207+const 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+
216+const 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+
111224 export const createRuntimeControlPlaneLayer = (
112− input: RuntimeControlPlaneOptions & {
225+ input: RuntimeControlPlaneOptions & RuntimeControlPlaneServices & {
113226 store: ControlPlaneStoreShape;
114227 localWorkspaceState: RuntimeLocalWorkspaceState;
115228 liveExecutionManager: ReturnType<typeof createLiveExecutionManager>;
116229 },
117230 ) => {
118− const platformLayer = NodeFileSystem.layer;
119− const storageLayer = LocalStorageLive.pipe(Layer.provide(platformLayer));
120− const localToolRuntimeLayer = LocalToolRuntimeLoaderLive.pipe(
121− Layer.provide(platformLayer),
231+ const storageLayer = makeLocalStorageLayer({
232+ installationStore: toInstallationStoreShape(input.installationStore),
233+ workspaceConfigStore: toWorkspaceConfigStoreShape(input.workspaceConfigStore),
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+ }),
122243 );
123244
124245 const baseLayer = Layer.mergeAll(
125− platformLayer,
126246 Layer.succeed(ControlPlaneStore, input.store),
127247 RuntimeLocalWorkspaceLive(input.localWorkspaceState),
128248 storageLayer,
129249 Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager),
130250 );
131251
132− const secretMaterialLayer = SecretMaterialLive({
133− resolveSecretMaterial: input.resolveSecretMaterial,
134− }).pipe(Layer.provide(baseLayer));
135−
136− const sourceStoreLayer = RuntimeSourceStoreLive.pipe(
252+ const secretMaterialLayer = makeSecretMaterialLayer(input.secretMaterial).pipe(
137253 Layer.provide(baseLayer),
138254 );
139255
256+ const sourceStoreLayer = RuntimeSourceStoreLive.pipe(Layer.provide(baseLayer));
257+
140258 const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe(
141259 Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)),
142260 );
@@ -203,73 +321,50 @@ export type ControlPlaneRuntime = { · 60 unmodified lines
203321 close: () => Promise<void>;
204322 };
205323
206−export type CreateControlPlaneRuntimeOptions = RuntimeControlPlaneOptions;
207−
208324 export const provideControlPlaneRuntime = <A, E, R>(
209325 effect: Effect.Effect<A, E, R>,
210326 runtime: ControlPlaneRuntime,
211−): Effect.Effect<A, E | never, any> =>
327+): Effect.Effect<A, E | never, never> =>
212328 effect.pipe(Effect.provide(runtime.managedRuntime));
213329
214−export const createControlPlaneRuntime = (
215− options: CreateControlPlaneRuntimeOptions,
216−): Effect.Effect<ControlPlaneRuntime, Error> =>
330+export const createControlPlaneRuntimeFromServices = (input: {
331+ services: RuntimeControlPlaneServices;
332+} & RuntimeControlPlaneOptions): Effect.Effect<ControlPlaneRuntime, Error> =>
217333 (Effect.gen(function* () {
218− const scope = yield* Scope.make();
334+ const localInstallation = yield* input.services.installationStore
335+ .getOrProvision()
336+ .pipe(Effect.mapError(toRuntimeBootstrapError));
219337
220− const localWorkspaceContext = yield* resolveLocalWorkspaceContext({
221− workspaceRoot: options.workspaceRoot,
222− homeConfigPath: options.homeConfigPath,
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− );
338+ const loadedLocalConfig = yield* input.services.workspaceConfigStore
339+ .load()
340+ .pipe(Effect.mapError(toRuntimeBootstrapError));
260341
261342 const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({
262− context: localWorkspaceContext,
343+ context: input.services.workspaceContext,
263344 loadedConfig: loadedLocalConfig,
264− }).pipe(
265− Effect.mapError(toLocalRuntimeBootstrapError),
266− Effect.catchAll((error) =>
267− closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))),
268− ),
269− );
345+ })
346+ .pipe(
347+ Effect.provide(
348+ makeLocalStorageLayer({
349+ installationStore: toInstallationStoreShape(
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));
270365
271366 const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = {
272− context: localWorkspaceContext,
367+ context: input.services.workspaceContext,
273368 installation: {
274369 workspaceId: localInstallation.workspaceId,
275370 accountId: localInstallation.accountId,
@@ -282,8 +377,9 @@ export const createControlPlaneRuntime = ( · 6 unmodified lines
282377 const liveExecutionManager = createLiveExecutionManager();
283378
284379 const concreteRuntimeLayer = createRuntimeControlPlaneLayer({
285− ...options,
286− store: rows,
380+ ...input,
381+ ...input.services,
382+ store: input.services.persistence.rows,
287383 localWorkspaceState: runtimeLocalWorkspaceState,
288384 liveExecutionManager,
289385 });
@@ -298,7 +394,7 @@ export const createControlPlaneRuntime = ( · 8 unmodified lines
298394 );
299395
300396 return {
301− persistence,
397+ persistence: input.services.persistence,
302398 localInstallation,
303399 managedRuntime,
304400 runtimeLayer: concreteRuntimeLayer as RuntimeControlPlaneLayer,
@@ -307,9 +403,7 @@ export const createControlPlaneRuntime = ( · 2 unmodified lines
307403 () => undefined,
308404 );
309405 await managedRuntime.dispose().catch(() => undefined);
406+ await input.services.persistence.close().catch(() => undefined);
310407 },
311408 };
312− }).pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect<
313− ControlPlaneRuntime,
314− Error
315− >);
409+ }) as Effect.Effect<ControlPlaneRuntime, Error>);
packages/platform/sdk/src/runtime/local/installation.test.ts+1−1
@@ -5,7 +5,7 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines
55 import { describe, expect, it } from "@effect/vitest";
66 import * as Effect from "effect/Effect";
77
8−import { createControlPlaneRuntime } from "../index";
8+import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index";
99 import { getOrProvisionLocalInstallation } from "./installation";
1010 import { resolveLocalWorkspaceContext } from "./config";
1111
packages/platform/sdk/src/runtime/local/storage.ts+2−2
@@ -38,10 +38,10 @@ import type { Source } from "#schema"; · 37 unmodified lines
3838 export type InstallationStoreShape = {
3939 load: (
4040 context: ResolvedLocalWorkspaceContext,
41− ) => Effect.Effect<LocalInstallation, never, never>;
41+ ) => Effect.Effect<LocalInstallation, Error, never>;
4242 getOrProvision: (input: {
4343 context: ResolvedLocalWorkspaceContext;
44− }) => Effect.Effect<LocalInstallation, never, never>;
44+ }) => Effect.Effect<LocalInstallation, Error, never>;
4545 };
4646
4747 export 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
2222 import type { RuntimeLocalWorkspaceState } from "../local/runtime-context";
2323 import {
2424 type LocalStorageServices,
25− LocalInstallationStore,
26− LocalSourceArtifactStore,
27− LocalWorkspaceConfigStore,
28− LocalWorkspaceStateStore,
25+ type InstallationStoreShape,
26+ type SourceArtifactStoreShape,
27+ type WorkspaceConfigStoreShape,
28+ type WorkspaceStateStoreShape,
2929 makeLocalStorageLayer,
3030 } from "../local/storage";
3131 import { provideOptionalRuntimeLocalWorkspace } from "../local/runtime-context";
@@ -34,14 +34,15 @@ import { runtimeEffectError } from "../effect-errors"; · 2 unmodified lines
3434 /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */
3535 const runEffect = async <A>(
3636 effect: Effect.Effect<A, unknown, LocalStorageServices>,
37+ storage: {
38+ installationStore: InstallationStoreShape;
39+ workspaceConfigStore: WorkspaceConfigStoreShape;
40+ workspaceStateStore: WorkspaceStateStoreShape;
41+ sourceArtifactStore: SourceArtifactStoreShape;
42+ },
3743 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null,
3844 ): Promise<A> => {
39− const baseLayer = makeLocalStorageLayer({
40− installationStore: LocalInstallationStore,
41− workspaceConfigStore: LocalWorkspaceConfigStore,
42− workspaceStateStore: LocalWorkspaceStateStore,
43− sourceArtifactStore: LocalSourceArtifactStore,
44− });
45+ const baseLayer = makeLocalStorageLayer(storage);
4546 const exit = await Effect.runPromiseExit(
4647 provideOptionalRuntimeLocalWorkspace(
4748 effect.pipe(Effect.provide(baseLayer)),
@@ -314,6 +315,10 @@ export const createExecutorToolMap = (input: { · 266 unmodified lines
314315 workspaceId: WorkspaceId;
315316 accountId: AccountId;
316317 sourceAuthService: RuntimeSourceAuthService;
318+ installationStore: InstallationStoreShape;
319+ workspaceConfigStore: WorkspaceConfigStoreShape;
320+ workspaceStateStore: WorkspaceStateStoreShape;
321+ sourceArtifactStore: SourceArtifactStoreShape;
317322 runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null;
318323 }): ToolMap => ({
319324 "executor.sources.add": toTool({
@@ -349,6 +354,12 @@ export const createExecutorToolMap = (input: { · 29 unmodified lines
349354 }
350355 : undefined,
351356 ),
357+ {
358+ installationStore: input.installationStore,
359+ workspaceConfigStore: input.workspaceConfigStore,
360+ workspaceStateStore: input.workspaceStateStore,
361+ sourceArtifactStore: input.sourceArtifactStore,
362+ },
352363 input.runtimeLocalWorkspace,
353364 );
354365
@@ -382,6 +393,12 @@ export const createExecutorToolMap = (input: { · 27 unmodified lines
382393 invocation: context?.invocation,
383394 onElicitation: context?.onElicitation,
384395 }),
396+ {
397+ installationStore: input.installationStore,
398+ workspaceConfigStore: input.workspaceConfigStore,
399+ workspaceStateStore: input.workspaceStateStore,
400+ sourceArtifactStore: input.sourceArtifactStore,
401+ },
385402 input.runtimeLocalWorkspace,
386403 );
387404
@@ -412,6 +429,12 @@ export const createExecutorToolMap = (input: { · 24 unmodified lines
412429 }
413430 : undefined,
414431 ),
432+ {
433+ installationStore: input.installationStore,
434+ workspaceConfigStore: input.workspaceConfigStore,
435+ workspaceStateStore: input.workspaceStateStore,
436+ sourceArtifactStore: input.sourceArtifactStore,
437+ },
415438 input.runtimeLocalWorkspace,
416439 );
417440
@@ -456,6 +479,12 @@ export const createExecutorToolMap = (input: { · 38 unmodified lines
456479 elicitationId: result.sessionId,
457480 },
458481 }),
482+ {
483+ installationStore: input.installationStore,
484+ workspaceConfigStore: input.workspaceConfigStore,
485+ workspaceStateStore: input.workspaceStateStore,
486+ sourceArtifactStore: input.sourceArtifactStore,
487+ },
459488 input.runtimeLocalWorkspace,
460489 );
461490
@@ -469,6 +498,12 @@ export const createExecutorToolMap = (input: { · 7 unmodified lines
469498 sourceId: result.source.id,
470499 actorAccountId: input.accountId,
471500 }),
501+ {
502+ installationStore: input.installationStore,
503+ workspaceConfigStore: input.workspaceConfigStore,
504+ workspaceStateStore: input.workspaceStateStore,
505+ sourceArtifactStore: input.sourceArtifactStore,
506+ },
472507 input.runtimeLocalWorkspace,
473508 );
474509 return toSerializableValue(connected);
packages/platform/sdk/src/schema/index.ts+0−1
@@ -2,7 +2,6 @@ export * from "./common"; · 1 unmodified line
22 export * from "./enums";
33 export * from "./ids";
44
5−export * from "./models/auth";
65 export * from "./models/source";
76 export * from "./models/source-catalog";
87 export * from "./models/auth-artifact";
packages/platform/sdk/src/schema/models/auth/account.ts+0−29
@@ -1,29 +0,0 @@
1−import { Schema } from "effect";
2−
3−import { TimestampMsSchema } from "../../common";
4−import { AccountIdSchema } from "../../ids";
5−import { PrincipalProviderSchema } from "./principal";
6−
7−export 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−
17−export const AccountInsertSchema = AccountSchema;
18−
19−export 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−
29−export type Account = typeof AccountSchema.Type;
packages/platform/sdk/src/schema/models/auth/index.ts+0−2
@@ -1,2 +0,0 @@
1−export * from "./account";
2−export * from "./principal";
packages/platform/sdk/src/schema/models/auth/principal.ts+0−20
@@ -1,20 +0,0 @@
1−import { Schema } from "effect";
2−
3−import { AccountIdSchema } from "../../ids";
4−
5−export const PrincipalProviderSchema = Schema.Literal(
6− "local",
7− "workos",
8− "service",
9−);
10−
11−export 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−
19−export type PrincipalProvider = typeof PrincipalProviderSchema.Type;
20−export type Principal = typeof PrincipalSchema.Type;
packages/platform/server/package.json+1−0
@@ -18,6 +18,7 @@ · 17 unmodified lines
1818 "@executor/platform-api": "workspace:*",
1919 "@executor/platform-internal": "workspace:*",
2020 "@executor/platform-sdk": "workspace:*",
21+ "@executor/platform-sdk-file": "workspace:*",
2122 "@opentelemetry/api": "^1.9.0",
2223 "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
2324 "@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
99 import { FileSystem, HttpApiBuilder, HttpServer } from "@effect/platform";
1010 import { NodeFileSystem } from "@effect/platform-node";
1111 import {
12− createControlPlaneApiLayer,
12+ createExecutorApiLayer,
1313 } from "@executor/platform-api";
1414 import { createExecutorMcpRequestHandler } from "@executor/executor-mcp";
15−import { createExecutorAdminToolMap } from "@executor/platform-internal";
15+import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
16+import {
17+ createExecutorEffect,
18+ type Executor,
19+} from "@executor/platform-sdk";
20+import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
1621 import {
17− createControlPlaneRuntime,
1822 type ResolveExecutionEnvironment,
1923 type ResolveSecretMaterial,
2024 type ControlPlaneRuntime,
@@ -71,6 +75,7 @@ type StaticUiOptions = { · 50 unmodified lines
7175 };
7276
7377 export type LocalExecutorServer = {
78+ readonly executor: Executor;
7479 readonly runtime: ControlPlaneRuntime;
7580 readonly port: number;
7681 readonly host: string;
@@ -89,6 +94,7 @@ export type StartLocalExecutorServerOptions = { · 12 unmodified lines
8994 };
9095
9196 export type LocalExecutorRequestHandler = {
97+ readonly executor: Executor;
9298 readonly runtime: ControlPlaneRuntime;
9399 readonly handleApiRequest: (request: Request) => Promise<Response>;
94100 readonly getBaseUrl: () => string | undefined;
@@ -102,25 +108,27 @@ const EXECUTOR_NPM_DIST_TAGS_PATHNAME = "/v1/app/npm/dist-tags"; · 7 unmodified lines
102108 const EXECUTOR_NPM_DIST_TAGS_URL =
103109 "https://registry.npmjs.org/-/package/executor/dist-tags";
104110
105−const disposeRuntime = (runtime: ControlPlaneRuntime) =>
111+const disposeExecutor = (executor: Executor) =>
106112 Effect.tryPromise({
107− try: () => runtime.close(),
113+ try: () => executor.close(),
108114 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")),
110116 }).pipe(Effect.orDie);
111117
112−const createRuntime = (
118+const createExecutorRuntime = (
113119 localDataDir: string,
114120 getLocalServerBaseUrl: () => string | undefined,
115121 options: StartLocalExecutorServerOptions,
116122 ) =>
117− createControlPlaneRuntime({
118− workspaceRoot: options.workspaceRoot,
123+ createExecutorEffect({
124+ backend: createLocalExecutorBackend({
125+ workspaceRoot: options.workspaceRoot,
126+ localDataDir,
127+ }),
119128 executionResolver: options.executionResolver,
120− createInternalToolMap: createExecutorAdminToolMap,
129+ createInternalToolMap: createWorkspaceExecutorAdminToolMap,
121130 resolveSecretMaterial: options.resolveSecretMaterial,
122131 getLocalServerBaseUrl,
123− localDataDir,
124132 }).pipe(
125133 Effect.mapError((cause) =>
126134 cause instanceof Error ? cause : new Error(String(cause)),
@@ -128,7 +136,7 @@ const createRuntime = ( · 1 unmodified line
128136 );
129137
130138 const createControlPlaneWebHandler = (
131− runtime: ControlPlaneRuntime,
139+ executor: Executor,
132140 tracingRuntime: ReturnType<typeof createLocalTracingRuntimeFromEnv>,
133141 ) =>
134142 Effect.acquireRelease(
@@ -137,7 +145,7 @@ const createControlPlaneWebHandler = ( · 2 unmodified lines
137145 Layer.merge(
138146 HttpApiBuilder.middlewareOpenApi({ path: "/v1/openapi.json" }).pipe(
139147 Layer.provideMerge(
140− createControlPlaneApiLayer(runtime.runtimeLayer).pipe(
148+ createExecutorApiLayer(executor).pipe(
141149 Layer.provideMerge(tracingRuntime?.layer ?? Layer.empty),
142150 ),
143151 )
@@ -369,14 +377,17 @@ export const createLocalExecutorRequestHandler = ( · 225 unmodified lines
369377 });
370378 }
371379
372− const runtime = yield* Effect.acquireRelease(
373− createRuntime(requestedLocalDataDir, () => baseUrlRef, options),
374− disposeRuntime,
380+ const executor = yield* Effect.acquireRelease(
381+ createExecutorRuntime(requestedLocalDataDir, () => baseUrlRef, options),
382+ disposeExecutor,
375383 );
376384
377− const apiHandler = yield* createControlPlaneWebHandler(runtime, tracingRuntime);
385+ const apiHandler = yield* createControlPlaneWebHandler(
386+ executor,
387+ tracingRuntime,
388+ );
378389 const mcpHandler = yield* Effect.acquireRelease(
379− Effect.sync(() => createExecutorMcpRequestHandler(runtime)),
390+ Effect.sync(() => createExecutorMcpRequestHandler(executor.runtime)),
380391 (handler: ExecutorMcpHandler) =>
381392 Effect.tryPromise({
382393 try: () => handler.close(),
@@ -388,7 +399,8 @@ export const createLocalExecutorRequestHandler = ( · 5 unmodified lines
388399 );
389400
390401 return {
391− runtime,
402+ executor,
403+ runtime: executor.runtime,
392404 handleApiRequest: (request) => {
393405 const pathname = new URL(request.url).pathname;
394406 if (pathname === EXECUTOR_NPM_DIST_TAGS_PATHNAME) {
@@ -465,6 +477,7 @@ export const createLocalExecutorServer = ( · 70 unmodified lines
465477 requestHandler.setBaseUrl(baseUrl);
466478
467479 return {
480+ executor: requestHandler.executor,
468481 runtime: requestHandler.runtime,
469482 host,
470483 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