History

Split promise and effect public APIs

Rhys Sullivan committed 7 months ago

Commit f1ed20d1 parent 6fb3ea933 files · +973 −805Browse files

Unified · Split

33 files
  1. apps/executor/src/cli/dev.ts +1 −1
  2. apps/executor/src/cli/main.ts +5 −5
  3. apps/executor/src/server/server.real-ingestion.test.ts +2 −2
  4. apps/executor/src/server/server.test.ts +4 −2
  5. packages/clients/react/src/index.test.tsx +3 −3
  6. packages/clients/react/src/index.ts +17 −22
  7. packages/platform/api/package.json +1 −0
  8. packages/platform/api/src/client.ts +190 −13
  9. packages/platform/api/src/effect.ts +19 −0
  10. packages/platform/api/src/executions/http.ts +3 −5
  11. packages/platform/api/src/executor-context.ts +3 −3
  12. packages/platform/api/src/http.ts +2 −2
  13. packages/platform/api/src/index.ts +0 −14
  14. packages/platform/api/src/local/http.ts +6 −6
  15. packages/platform/api/src/oauth/http.ts +2 −2
  16. packages/platform/api/src/policies/http.ts +6 −10
  17. packages/platform/api/src/sources/http.ts +22 −26
  18. packages/platform/sdk-file/package.json +3 −1
  19. packages/platform/sdk-file/src/client.ts +5 −0
  20. packages/platform/sdk-file/src/effect.ts +28 −0
  21. packages/platform/sdk-file/src/index.ts +14 −4
  22. packages/platform/sdk-file/src/runtime.ts +2 −0
  23. packages/platform/sdk/package.json +1 −0
  24. packages/platform/sdk/src/effect.ts +1 −0
  25. packages/platform/sdk/src/executor-effect.ts +533 −0
  26. packages/platform/sdk/src/executor.test.ts +8 −4
  27. packages/platform/sdk/src/executor.ts +70 −664
  28. packages/platform/sdk/src/runtime/execution/http.test.ts +3 −3
  29. packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts +3 −3
  30. packages/platform/sdk/src/runtime/execution/runtime-config.test.ts +4 −4
  31. packages/platform/sdk/src/runtime/execution/test-http-client.ts +5 −1
  32. packages/platform/sdk/src/runtime/executor-runtime.test.ts +4 −2
  33. packages/platform/server/src/index.ts +3 −3
apps/executor/src/cli/dev.ts+1−1
@@ -1,4 +1,4 @@
1import type { ExecutorApiClient } from "@executor/platform-api";1import type { ExecutorApiEffectClient as ExecutorApiClient } from "@executor/platform-api/effect";
2import { ScopeIdSchema } from "@executor/platform-sdk/schema";2import { ScopeIdSchema } from "@executor/platform-sdk/schema";
3import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
4 4
apps/executor/src/cli/main.ts+5−5
@@ -10,9 +10,9 @@ import { · 9 unmodified lines
10 NodeRuntime,10 NodeRuntime,
11} from "@effect/platform-node";11} from "@effect/platform-node";
12import {12import {
13 createExecutorApiClient,13 createExecutorApiEffectClient as createExecutorApiClient,
14 type ExecutorApiClient,14 type ExecutorApiEffectClient as ExecutorApiClient,
15} from "@executor/platform-api";15} from "@executor/platform-api/effect";
16import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";16import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
17import {17import {
18 EXECUTOR_SOURCES_ADD_HELP_LINES,18 EXECUTOR_SOURCES_ADD_HELP_LINES,
@@ -20,8 +20,8 @@ import { · 1 unmodified line
20} from "@executor/platform-sdk/runtime";20} from "@executor/platform-sdk/runtime";
21import {21import {
22 createExecutorEffect,22 createExecutorEffect,
23 type Executor,23 type ExecutorEffect as Executor,
24} from "@executor/platform-sdk";24} from "@executor/platform-sdk/effect";
25import { createLocalExecutorBackend } from "@executor/platform-sdk-file";25import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
26import {26import {
27 ExecutionIdSchema,27 ExecutionIdSchema,
apps/executor/src/server/server.real-ingestion.test.ts+2−2
@@ -4,8 +4,8 @@ import { describe, expect, it } from "@effect/vitest"; · 3 unmodified lines
4import * as Effect from "effect/Effect";4import * as Effect from "effect/Effect";
5 5
6import {6import {
7 createExecutorApiClient,7 createExecutorApiEffectClient as createExecutorApiClient,
8} from "@executor/platform-api";8} from "@executor/platform-api/effect";
9import { type SourceDiscoveryKind } from "@executor/platform-sdk/schema";9import { type SourceDiscoveryKind } from "@executor/platform-sdk/schema";
10import { createLocalExecutorServer } from "@executor/server";10import { createLocalExecutorServer } from "@executor/server";
11 11
apps/executor/src/server/server.test.ts+4−2
@@ -20,9 +20,11 @@ import { startMcpElicitationDemoServer } from "@executor/mcp-elicitation-demo"; · 19 unmodified lines
20import { makeToolInvokerFromTools, toTool } from "@executor/codemode-core";20import { makeToolInvokerFromTools, toTool } from "@executor/codemode-core";
21import { startOpenApiTestServer } from "@executor/effect-test-utils";21import { startOpenApiTestServer } from "@executor/effect-test-utils";
22import {22import {
23 createExecutorApiClient,
24 executorOpenApiSpec,23 executorOpenApiSpec,
25} from "@executor/platform-api";24} from "@executor/platform-api";
25import {
26 createExecutorApiEffectClient as createExecutorApiClient,
27} from "@executor/platform-api/effect";
26import {28import {
27 catalogSyncResultFromMcpManifest,29 catalogSyncResultFromMcpManifest,
28 type ResolveExecutionEnvironment,30 type ResolveExecutionEnvironment,
@@ -33,7 +35,7 @@ import { · 4 unmodified lines
33 resolveLocalWorkspaceContext,35 resolveLocalWorkspaceContext,
34 writeLocalSourceArtifact,36 writeLocalSourceArtifact,
35 writeProjectLocalExecutorConfig,37 writeProjectLocalExecutorConfig,
36} from "@executor/platform-sdk-file";38} from "@executor/platform-sdk-file/effect";
37import { SourceIdSchema } from "@executor/platform-sdk/schema";39import { SourceIdSchema } from "@executor/platform-sdk/schema";
38import { makeSesExecutor } from "@executor/runtime-ses";40import { makeSesExecutor } from "@executor/runtime-ses";
39import { Client } from "@modelcontextprotocol/sdk/client/index.js";41import { Client } from "@modelcontextprotocol/sdk/client/index.js";
packages/clients/react/src/index.test.tsx+3−3
@@ -16,10 +16,10 @@ import { · 15 unmodified lines
16} from "@executor/effect-test-utils";16} from "@executor/effect-test-utils";
17import {17import {
18 createExecutorApiLayer,18 createExecutorApiLayer,
19} from "@executor/platform-api";19} from "@executor/platform-api/http";
20import { createExecutorEffect } from "@executor/platform-sdk";20import { createExecutorEffect } from "@executor/platform-sdk/effect";
21import type { ExecutorRuntime } from "@executor/platform-sdk/runtime";21import type { ExecutorRuntime } from "@executor/platform-sdk/runtime";
22import { createLocalExecutorRuntime as createExecutorRuntime } from "@executor/platform-sdk-file";22import { createLocalExecutorRuntime as createExecutorRuntime } from "@executor/platform-sdk-file/runtime";
23import type {23import type {
24 LocalInstallation,24 LocalInstallation,
25 Source,25 Source,
packages/clients/react/src/index.ts+17−22
@@ -36,7 +36,6 @@ import type { · 35 unmodified lines
36} from "@executor/platform-sdk/schema";36} from "@executor/platform-sdk/schema";
37import * as Cause from "effect/Cause";37import * as Cause from "effect/Cause";
38import * as Effect from "effect/Effect";38import * as Effect from "effect/Effect";
39import * as Exit from "effect/Exit";
40import * as Option from "effect/Option";39import * as Option from "effect/Option";
41import * as Runtime from "effect/Runtime";40import * as Runtime from "effect/Runtime";
42import * as React from "react";41import * as React from "react";
@@ -189,12 +188,12 @@ const encodeWorkspaceOauthClientsKey = ( · 146 unmodified lines
189 [enabled, workspaceId, accountId, providerKey] satisfies WorkspaceOauthClientsKeyParts,188 [enabled, workspaceId, accountId, providerKey] satisfies WorkspaceOauthClientsKeyParts,
190 );189 );
191 190
192const causeMessage = (cause: Cause.Cause<unknown>): Error =>
193 new Error(Cause.pretty(cause));
194
195const toError = (cause: unknown): Error =>191const toError = (cause: unknown): Error =>
196 cause instanceof Error ? cause : new Error(String(cause));192 cause instanceof Error ? cause : new Error(String(cause));
197 193
194const causeMessage = (cause: Cause.Cause<unknown>): Error =>
195 new Error(Cause.pretty(cause));
196
198const shouldLogExecutorDevErrors = (): boolean => {197const shouldLogExecutorDevErrors = (): boolean => {
199 if (typeof window === "undefined") {198 if (typeof window === "undefined") {
200 return false;199 return false;
@@ -238,36 +237,32 @@ const logExecutorDevError = (label: string, details: Record<string, unknown>): v · 37 unmodified lines
238const runControlPlane = async <A>(input: {237const runControlPlane = async <A>(input: {
239 baseUrl?: string;238 baseUrl?: string;
240 accountId?: string;239 accountId?: string;
241 execute: (client: ExecutorApiClient) => Effect.Effect<A, unknown, never>;240 execute: (client: ExecutorApiClient) => Promise<A>;
242}): Promise<A> => {241}): Promise<A> => {
243 const baseUrl = input.baseUrl ?? apiBaseUrl;242 const baseUrl = input.baseUrl ?? apiBaseUrl;
244 const accountId = input.accountId;243 const accountId = input.accountId;
245 244
246 const exit = await Effect.runPromiseExit(245 try {
247 createExecutorApiClient({246 const client = await createExecutorApiClient({
248 baseUrl,247 baseUrl,
249 ...(accountId !== undefined ? { accountId } : {}),248 ...(accountId !== undefined ? { accountId } : {}),
250 }).pipe(Effect.flatMap(input.execute)),249 });
251 );250 return await input.execute(client);
252 251 } catch (cause) {
253 if (Exit.isSuccess(exit)) {252 const error = toError(cause);
254 return exit.value;253 logExecutorDevError("control-plane request failed", {
254 baseUrl,
255 accountId,
256 error: describeExecutorDevError(error),
257 });
258 throw error;
255 }259 }
256
257 const error = Cause.squash(exit.cause);
258 logExecutorDevError("control-plane request failed", {
259 baseUrl,
260 accountId,
261 error: describeExecutorDevError(error),
262 cause: Cause.pretty(exit.cause),
263 });
264 throw error;
265};260};
266 261
267const controlPlaneRequest = <A>(input: {262const controlPlaneRequest = <A>(input: {
268 baseUrl?: string;263 baseUrl?: string;
269 accountId?: string;264 accountId?: string;
270 execute: (client: ExecutorApiClient) => Effect.Effect<A, unknown, never>;265 execute: (client: ExecutorApiClient) => Promise<A>;
271}): Effect.Effect<A, Error> =>266}): Effect.Effect<A, Error> =>
272 Effect.tryPromise({267 Effect.tryPromise({
273 try: () => runControlPlane(input),268 try: () => runControlPlane(input),
packages/platform/api/package.json+1−0
@@ -6,6 +6,7 @@ · 5 unmodified lines
6 ".": "./src/index.ts",6 ".": "./src/index.ts",
7 "./api": "./src/api.ts",7 "./api": "./src/api.ts",
8 "./client": "./src/client.ts",8 "./client": "./src/client.ts",
9 "./effect": "./src/effect.ts",
9 "./http": "./src/http.ts",10 "./http": "./src/http.ts",
10 "./errors": "./src/errors.ts",11 "./errors": "./src/errors.ts",
11 "./local/api": "./src/local/api.ts",12 "./local/api": "./src/local/api.ts",
packages/platform/api/src/client.ts+190−13
@@ -1,19 +1,196 @@
1import {
2 FetchHttpClient,
3 HttpApiClient,
4} from "@effect/platform";
5import * as Effect from "effect/Effect";1import * as Effect from "effect/Effect";
6 2
7import { ExecutorApi } from "./api";3import {
4 createExecutorApiEffectClient,
5 type ExecutorApiEffectClient,
6} from "./effect";
7
8type StripResponse<T> = T extends readonly [infer Value, unknown] ? Value : T;
9type RemoveWithResponse<T> = T extends object ? Omit<T, "withResponse"> : T;
10type MethodInput<F extends (input: any) => Effect.Effect<any, any, any>> = RemoveWithResponse<
11 Parameters<F>[0]
12>;
13type MethodOutput<F extends (input: any) => Effect.Effect<any, any, any>> = StripResponse<
14 Effect.Effect.Success<ReturnType<F>>
15>;
16type PromiseMethod<F extends (input: any) => Effect.Effect<any, any, any>> = (
17 input: MethodInput<F>,
18) => Promise<MethodOutput<F>>;
19
20export type ExecutorApiClient = {
21 local: {
22 installation: PromiseMethod<ExecutorApiEffectClient["local"]["installation"]>;
23 config: PromiseMethod<ExecutorApiEffectClient["local"]["config"]>;
24 listSecrets: PromiseMethod<ExecutorApiEffectClient["local"]["listSecrets"]>;
25 createSecret: PromiseMethod<ExecutorApiEffectClient["local"]["createSecret"]>;
26 updateSecret: PromiseMethod<ExecutorApiEffectClient["local"]["updateSecret"]>;
27 deleteSecret: PromiseMethod<ExecutorApiEffectClient["local"]["deleteSecret"]>;
28 };
29 policies: {
30 list: PromiseMethod<ExecutorApiEffectClient["policies"]["list"]>;
31 create: PromiseMethod<ExecutorApiEffectClient["policies"]["create"]>;
32 get: PromiseMethod<ExecutorApiEffectClient["policies"]["get"]>;
33 update: PromiseMethod<ExecutorApiEffectClient["policies"]["update"]>;
34 remove: PromiseMethod<ExecutorApiEffectClient["policies"]["remove"]>;
35 };
36 sources: {
37 discover: PromiseMethod<ExecutorApiEffectClient["sources"]["discover"]>;
38 connect: PromiseMethod<ExecutorApiEffectClient["sources"]["connect"]>;
39 connectBatch: PromiseMethod<ExecutorApiEffectClient["sources"]["connectBatch"]>;
40 listWorkspaceOauthClients: PromiseMethod<
41 ExecutorApiEffectClient["sources"]["listWorkspaceOauthClients"]
42 >;
43 createWorkspaceOauthClient: PromiseMethod<
44 ExecutorApiEffectClient["sources"]["createWorkspaceOauthClient"]
45 >;
46 removeWorkspaceOauthClient: PromiseMethod<
47 ExecutorApiEffectClient["sources"]["removeWorkspaceOauthClient"]
48 >;
49 removeProviderAuthGrant: PromiseMethod<
50 ExecutorApiEffectClient["sources"]["removeProviderAuthGrant"]
51 >;
52 list: PromiseMethod<ExecutorApiEffectClient["sources"]["list"]>;
53 create: PromiseMethod<ExecutorApiEffectClient["sources"]["create"]>;
54 get: PromiseMethod<ExecutorApiEffectClient["sources"]["get"]>;
55 update: PromiseMethod<ExecutorApiEffectClient["sources"]["update"]>;
56 remove: PromiseMethod<ExecutorApiEffectClient["sources"]["remove"]>;
57 inspection: PromiseMethod<ExecutorApiEffectClient["sources"]["inspection"]>;
58 inspectionTool: PromiseMethod<ExecutorApiEffectClient["sources"]["inspectionTool"]>;
59 inspectionDiscover: PromiseMethod<
60 ExecutorApiEffectClient["sources"]["inspectionDiscover"]
61 >;
62 credentialPage: PromiseMethod<ExecutorApiEffectClient["sources"]["credentialPage"]>;
63 credentialSubmit: PromiseMethod<ExecutorApiEffectClient["sources"]["credentialSubmit"]>;
64 credentialComplete: PromiseMethod<
65 ExecutorApiEffectClient["sources"]["credentialComplete"]
66 >;
67 providerOauthComplete: PromiseMethod<
68 ExecutorApiEffectClient["sources"]["providerOauthComplete"]
69 >;
70 };
71 oauth: {
72 startSourceAuth: PromiseMethod<ExecutorApiEffectClient["oauth"]["startSourceAuth"]>;
73 sourceAuthCallback: PromiseMethod<ExecutorApiEffectClient["oauth"]["sourceAuthCallback"]>;
74 };
75 executions: {
76 create: PromiseMethod<ExecutorApiEffectClient["executions"]["create"]>;
77 get: PromiseMethod<ExecutorApiEffectClient["executions"]["get"]>;
78 resume: PromiseMethod<ExecutorApiEffectClient["executions"]["resume"]>;
79 };
80};
8 81
9export const createExecutorApiClient = (input: {82const isResponseTuple = <Value>(
83 value: Value | readonly [Value, unknown],
84): value is readonly [Value, unknown] =>
85 Array.isArray(value) && value.length === 2;
86
87function stripResponse<Value>(value: Value | readonly [Value, unknown]): Value {
88 return isResponseTuple(value) ? value[0] : value;
89}
90
91const wrapMethod = <
92 F extends (input: any) => Effect.Effect<any, unknown, never>,
93>(
94 effectClientPromise: Promise<ExecutorApiEffectClient>,
95 select: (client: ExecutorApiEffectClient) => F,
96): PromiseMethod<F> => {
97 const wrapped = (input: MethodInput<F>): Promise<MethodOutput<F>> =>
98 effectClientPromise
99 .then((client) => Effect.runPromise(select(client)(input)))
100 .then(stripResponse);
101
102 return wrapped;
103};
104
105export const createExecutorApiClient = async (input: {
10 baseUrl: string;106 baseUrl: string;
11 accountId?: string;107 accountId?: string;
12}) =>108}): Promise<ExecutorApiClient> => {
13 HttpApiClient.make(ExecutorApi, {109 const effectClientPromise = Effect.runPromise(
14 baseUrl: input.baseUrl,110 createExecutorApiEffectClient(input),
15 }).pipe(Effect.provide(FetchHttpClient.layer));111 );
16 112
17export type ExecutorApiClient = Effect.Effect.Success<113 return {
18 ReturnType<typeof createExecutorApiClient>114 local: {
19>;115 installation: wrapMethod(effectClientPromise, (client) => client.local.installation),
116 config: wrapMethod(effectClientPromise, (client) => client.local.config),
117 listSecrets: wrapMethod(effectClientPromise, (client) => client.local.listSecrets),
118 createSecret: wrapMethod(effectClientPromise, (client) => client.local.createSecret),
119 updateSecret: wrapMethod(effectClientPromise, (client) => client.local.updateSecret),
120 deleteSecret: wrapMethod(effectClientPromise, (client) => client.local.deleteSecret),
121 },
122 policies: {
123 list: wrapMethod(effectClientPromise, (client) => client.policies.list),
124 create: wrapMethod(effectClientPromise, (client) => client.policies.create),
125 get: wrapMethod(effectClientPromise, (client) => client.policies.get),
126 update: wrapMethod(effectClientPromise, (client) => client.policies.update),
127 remove: wrapMethod(effectClientPromise, (client) => client.policies.remove),
128 },
129 sources: {
130 discover: wrapMethod(effectClientPromise, (client) => client.sources.discover),
131 connect: wrapMethod(effectClientPromise, (client) => client.sources.connect),
132 connectBatch: wrapMethod(effectClientPromise, (client) => client.sources.connectBatch),
133 listWorkspaceOauthClients: wrapMethod(
134 effectClientPromise,
135 (client) => client.sources.listWorkspaceOauthClients,
136 ),
137 createWorkspaceOauthClient: wrapMethod(
138 effectClientPromise,
139 (client) => client.sources.createWorkspaceOauthClient,
140 ),
141 removeWorkspaceOauthClient: wrapMethod(
142 effectClientPromise,
143 (client) => client.sources.removeWorkspaceOauthClient,
144 ),
145 removeProviderAuthGrant: wrapMethod(
146 effectClientPromise,
147 (client) => client.sources.removeProviderAuthGrant,
148 ),
149 list: wrapMethod(effectClientPromise, (client) => client.sources.list),
150 create: wrapMethod(effectClientPromise, (client) => client.sources.create),
151 get: wrapMethod(effectClientPromise, (client) => client.sources.get),
152 update: wrapMethod(effectClientPromise, (client) => client.sources.update),
153 remove: wrapMethod(effectClientPromise, (client) => client.sources.remove),
154 inspection: wrapMethod(effectClientPromise, (client) => client.sources.inspection),
155 inspectionTool: wrapMethod(
156 effectClientPromise,
157 (client) => client.sources.inspectionTool,
158 ),
159 inspectionDiscover: wrapMethod(
160 effectClientPromise,
161 (client) => client.sources.inspectionDiscover,
162 ),
163 credentialPage: wrapMethod(
164 effectClientPromise,
165 (client) => client.sources.credentialPage,
166 ),
167 credentialSubmit: wrapMethod(
168 effectClientPromise,
169 (client) => client.sources.credentialSubmit,
170 ),
171 credentialComplete: wrapMethod(
172 effectClientPromise,
173 (client) => client.sources.credentialComplete,
174 ),
175 providerOauthComplete: wrapMethod(
176 effectClientPromise,
177 (client) => client.sources.providerOauthComplete,
178 ),
179 },
180 oauth: {
181 startSourceAuth: wrapMethod(
182 effectClientPromise,
183 (client) => client.oauth.startSourceAuth,
184 ),
185 sourceAuthCallback: wrapMethod(
186 effectClientPromise,
187 (client) => client.oauth.sourceAuthCallback,
188 ),
189 },
190 executions: {
191 create: wrapMethod(effectClientPromise, (client) => client.executions.create),
192 get: wrapMethod(effectClientPromise, (client) => client.executions.get),
193 resume: wrapMethod(effectClientPromise, (client) => client.executions.resume),
194 },
195 };
196};
packages/platform/api/src/effect.ts+19−0
@@ -0,0 +1,19 @@
1import {
2 FetchHttpClient,
3 HttpApiClient,
4} from "@effect/platform";
5import * as Effect from "effect/Effect";
6
7import { ExecutorApi } from "./api";
8
9export const createExecutorApiEffectClient = (input: {
10 baseUrl: string;
11 accountId?: string;
12}) =>
13 HttpApiClient.make(ExecutorApi, {
14 baseUrl: input.baseUrl,
15 }).pipe(Effect.provide(FetchHttpClient.layer));
16
17export type ExecutorApiEffectClient = Effect.Effect.Success<
18 ReturnType<typeof createExecutorApiEffectClient>
19>;
packages/platform/api/src/executions/http.ts+3−5
@@ -11,19 +11,17 @@ export const ExecutorExecutionsLive = HttpApiBuilder.group( · 10 unmodified lines
11 handlers11 handlers
12 .handle("create", ({ path, payload }) =>12 .handle("create", ({ path, payload }) =>
13 resolveRequestedLocalWorkspace("executions.create", path.workspaceId).pipe(13 resolveRequestedLocalWorkspace("executions.create", path.workspaceId).pipe(
14 Effect.flatMap((executor) => executor.effect.executions.create(payload)),14 Effect.flatMap((executor) => executor.executions.create(payload)),
15 ),15 ),
16 )16 )
17 .handle("get", ({ path }) =>17 .handle("get", ({ path }) =>
18 resolveRequestedLocalWorkspace("executions.get", path.workspaceId).pipe(18 resolveRequestedLocalWorkspace("executions.get", path.workspaceId).pipe(
19 Effect.flatMap((executor) => executor.effect.executions.get(path.executionId)),19 Effect.flatMap((executor) => executor.executions.get(path.executionId)),
20 ),20 ),
21 )21 )
22 .handle("resume", ({ path, payload }) =>22 .handle("resume", ({ path, payload }) =>
23 resolveRequestedLocalWorkspace("executions.resume", path.workspaceId).pipe(23 resolveRequestedLocalWorkspace("executions.resume", path.workspaceId).pipe(
24 Effect.flatMap((executor) =>24 Effect.flatMap((executor) => executor.executions.resume(path.executionId, payload)),
25 executor.effect.executions.resume(path.executionId, payload)
26 ),
27 ),25 ),
28 ),26 ),
29);27);
packages/platform/api/src/executor-context.ts+3−3
@@ -1,4 +1,4 @@
1import type { Executor } from "@executor/platform-sdk";1import type { ExecutorEffect } from "@executor/platform-sdk/effect";
2import type { ScopeId as WorkspaceId } from "@executor/platform-sdk/schema";2import type { ScopeId as WorkspaceId } from "@executor/platform-sdk/schema";
3import {3import {
4 ControlPlaneBadRequestError,4 ControlPlaneBadRequestError,
@@ -11,9 +11,9 @@ import * as Layer from "effect/Layer"; · 6 unmodified lines
11 11
12export class ControlPlaneExecutor extends Context.Tag(12export class ControlPlaneExecutor extends Context.Tag(
13 "@executor/platform-api/ControlPlaneExecutor",13 "@executor/platform-api/ControlPlaneExecutor",
14)<ControlPlaneExecutor, Executor>() {}14)<ControlPlaneExecutor, ExecutorEffect>() {}
15 15
16export const createControlPlaneExecutorLayer = (executor: Executor) =>16export const createControlPlaneExecutorLayer = (executor: ExecutorEffect) =>
17 Layer.succeed(ControlPlaneExecutor, executor);17 Layer.succeed(ControlPlaneExecutor, executor);
18 18
19export const getControlPlaneExecutor = () => ControlPlaneExecutor;19export const getControlPlaneExecutor = () => ControlPlaneExecutor;
packages/platform/api/src/http.ts+2−2
@@ -1,6 +1,6 @@
1import { HttpApiBuilder } from "@effect/platform";1import { HttpApiBuilder } from "@effect/platform";
2import * as Layer from "effect/Layer";2import * as Layer from "effect/Layer";
3import type { Executor } from "@executor/platform-sdk";3import type { ExecutorEffect } from "@executor/platform-sdk/effect";
4 4
5import { ExecutorApi } from "./api";5import { ExecutorApi } from "./api";
6import { createControlPlaneExecutorLayer } from "./executor-context";6import { createControlPlaneExecutorLayer } from "./executor-context";
@@ -20,7 +20,7 @@ export const ExecutorApiLive = HttpApiBuilder.api(ExecutorApi).pipe( · 13 unmodified lines
20 20
21export type ExecutorApiRuntimeContext = Layer.Layer.Context<typeof ExecutorApiLive>;21export type ExecutorApiRuntimeContext = Layer.Layer.Context<typeof ExecutorApiLive>;
22 22
23export const createExecutorApiLayer = (executor: Executor) =>23export const createExecutorApiLayer = (executor: ExecutorEffect) =>
24 ExecutorApiLive.pipe(24 ExecutorApiLive.pipe(
25 Layer.provide(createControlPlaneExecutorLayer(executor)),25 Layer.provide(createControlPlaneExecutorLayer(executor)),
26 );26 );
packages/platform/api/src/index.ts+0−14
@@ -17,13 +17,6 @@ export { · 16 unmodified lines
17 ControlPlaneUnauthorizedError,17 ControlPlaneUnauthorizedError,
18} from "./errors";18} from "./errors";
19 19
20export {
21 ExecutorApiLive,
22 type ExecutorApiRuntimeContext,
23 type BuiltExecutorApiLayer,
24 createExecutorApiLayer,
25} from "./http";
26
27export {20export {
28 CreateExecutionPayloadSchema,21 CreateExecutionPayloadSchema,
29 ExecutionsApi,22 ExecutionsApi,
@@ -31,7 +24,6 @@ export { · 1 unmodified line
31 type CreateExecutionPayload,24 type CreateExecutionPayload,
32 type ResumeExecutionPayload,25 type ResumeExecutionPayload,
33} from "./executions/api";26} from "./executions/api";
34export { ExecutorExecutionsLive } from "./executions/http";
35 27
36export {28export {
37 LocalApi,29 LocalApi,
@@ -44,7 +36,6 @@ export { · 6 unmodified lines
44 type UpdateSecretResult,36 type UpdateSecretResult,
45 type DeleteSecretResult,37 type DeleteSecretResult,
46} from "./local/api";38} from "./local/api";
47export { ExecutorLocalLive } from "./local/http";
48 39
49export {40export {
50 OAuthApi,41 OAuthApi,
@@ -59,7 +50,6 @@ export { · 8 unmodified lines
59 type CompleteSourceOAuthResult,50 type CompleteSourceOAuthResult,
60 type SourceOAuthPopupResult,51 type SourceOAuthPopupResult,
61} from "./oauth/api";52} from "./oauth/api";
62export { ExecutorOAuthLive } from "./oauth/http";
63 53
64export {54export {
65 ConnectSourceBatchPayloadSchema,55 ConnectSourceBatchPayloadSchema,
@@ -80,7 +70,6 @@ export { · 14 unmodified lines
80 type DiscoverSourcePayload,70 type DiscoverSourcePayload,
81 type UpdateSourcePayload,71 type UpdateSourcePayload,
82} from "./sources/api";72} from "./sources/api";
83export { ExecutorSourcesLive } from "./sources/http";
84 73
85export {74export {
86 CreatePolicyPayloadSchema,75 CreatePolicyPayloadSchema,
@@ -89,6 +78,3 @@ export { · 2 unmodified lines
89 type CreatePolicyPayload,78 type CreatePolicyPayload,
90 type UpdatePolicyPayload,79 type UpdatePolicyPayload,
91} from "./policies/api";80} from "./policies/api";
92export { ExecutorPoliciesLive } from "./policies/http";
93
94export { resolveRequestedLocalWorkspace } from "./local-context";
packages/platform/api/src/local/http.ts+6−6
@@ -19,35 +19,35 @@ export const ExecutorLocalLive = HttpApiBuilder.group( · 18 unmodified lines
19 handlers19 handlers
20 .handle("installation", () =>20 .handle("installation", () =>
21 Effect.flatMap(getControlPlaneExecutor(), (executor) =>21 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
22 executor.effect.local.installation().pipe(22 executor.local.installation().pipe(
23 Effect.mapError(toStorageError("local.installation")),23 Effect.mapError(toStorageError("local.installation")),
24 )24 )
25 )25 )
26 )26 )
27 .handle("config", () =>27 .handle("config", () =>
28 Effect.flatMap(getControlPlaneExecutor(), (executor) =>28 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
29 executor.effect.local.config().pipe(29 executor.local.config().pipe(
30 Effect.mapError(toStorageError("local.config")),30 Effect.mapError(toStorageError("local.config")),
31 )31 )
32 )32 )
33 )33 )
34 .handle("listSecrets", () =>34 .handle("listSecrets", () =>
35 Effect.flatMap(getControlPlaneExecutor(), (executor) =>35 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
36 executor.effect.secrets.list().pipe(36 executor.secrets.list().pipe(
37 Effect.mapError(toStorageError("local.listSecrets")),37 Effect.mapError(toStorageError("local.listSecrets")),
38 )38 )
39 )39 )
40 )40 )
41 .handle("createSecret", ({ payload }) =>41 .handle("createSecret", ({ payload }) =>
42 Effect.flatMap(getControlPlaneExecutor(), (executor) =>42 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
43 executor.effect.secrets.create(payload).pipe(43 executor.secrets.create(payload).pipe(
44 Effect.mapError(toStorageError("local.createSecret")),44 Effect.mapError(toStorageError("local.createSecret")),
45 )45 )
46 )46 )
47 )47 )
48 .handle("updateSecret", ({ path, payload }) =>48 .handle("updateSecret", ({ path, payload }) =>
49 Effect.flatMap(getControlPlaneExecutor(), (executor) =>49 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
50 executor.effect.secrets.update({50 executor.secrets.update({
51 secretId: path.secretId,51 secretId: path.secretId,
52 payload,52 payload,
53 }).pipe(Effect.mapError(toStorageError("local.updateSecret")))53 }).pipe(Effect.mapError(toStorageError("local.updateSecret")))
@@ -55,7 +55,7 @@ export const ExecutorLocalLive = HttpApiBuilder.group( · 1 unmodified line
55 )55 )
56 .handle("deleteSecret", ({ path }) =>56 .handle("deleteSecret", ({ path }) =>
57 Effect.flatMap(getControlPlaneExecutor(), (executor) =>57 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
58 executor.effect.secrets.remove(path.secretId).pipe(58 executor.secrets.remove(path.secretId).pipe(
59 Effect.mapError(toStorageError("local.deleteSecret")),59 Effect.mapError(toStorageError("local.deleteSecret")),
60 )60 )
61 )61 )
packages/platform/api/src/oauth/http.ts+2−2
@@ -241,7 +241,7 @@ export const ExecutorOAuthLive = HttpApiBuilder.group( · 240 unmodified lines
241 Effect.flatMap((executor) =>241 Effect.flatMap((executor) =>
242 Effect.gen(function* () {242 Effect.gen(function* () {
243 const request = yield* HttpServerRequest.HttpServerRequest;243 const request = yield* HttpServerRequest.HttpServerRequest;
244 return yield* executor.effect.oauth.startSourceAuth({244 return yield* executor.oauth.startSourceAuth({
245 baseUrl: resolveRequestOrigin(request),245 baseUrl: resolveRequestOrigin(request),
246 displayName: payload.name,246 displayName: payload.name,
247 provider: {247 provider: {
@@ -271,7 +271,7 @@ export const ExecutorOAuthLive = HttpApiBuilder.group( · 23 unmodified lines
271 Effect.flatMap(getControlPlaneExecutor(), (executor) =>271 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
272 Effect.gen(function* () {272 Effect.gen(function* () {
273 const completed = yield* Effect.either(273 const completed = yield* Effect.either(
274 executor.effect.oauth.completeSourceAuth({274 executor.oauth.completeSourceAuth({
275 state: urlParams.state,275 state: urlParams.state,
276 code: urlParams.code,276 code: urlParams.code,
277 error: urlParams.error,277 error: urlParams.error,
packages/platform/api/src/policies/http.ts+6−10
@@ -11,7 +11,7 @@ export const ExecutorPoliciesLive = HttpApiBuilder.group( · 10 unmodified lines
11 handlers11 handlers
12 .handle("list", ({ path }) =>12 .handle("list", ({ path }) =>
13 resolveRequestedLocalWorkspace("policies.list", path.workspaceId).pipe(13 resolveRequestedLocalWorkspace("policies.list", path.workspaceId).pipe(
14 Effect.flatMap((executor) => executor.effect.policies.list()),14 Effect.flatMap((executor) => executor.policies.list()),
15 ),15 ),
16 )16 )
17 .handle("create", ({ path, payload }) =>17 .handle("create", ({ path, payload }) =>
@@ -19,12 +19,12 @@ export const ExecutorPoliciesLive = HttpApiBuilder.group( · 1 unmodified line
19 "policies.create",19 "policies.create",
20 path.workspaceId,20 path.workspaceId,
21 ).pipe(21 ).pipe(
22 Effect.flatMap((executor) => executor.effect.policies.create(payload)),22 Effect.flatMap((executor) => executor.policies.create(payload)),
23 ),23 ),
24 )24 )
25 .handle("get", ({ path }) =>25 .handle("get", ({ path }) =>
26 resolveRequestedLocalWorkspace("policies.get", path.workspaceId).pipe(26 resolveRequestedLocalWorkspace("policies.get", path.workspaceId).pipe(
27 Effect.flatMap((executor) => executor.effect.policies.get(path.policyId)),27 Effect.flatMap((executor) => executor.policies.get(path.policyId)),
28 ),28 ),
29 )29 )
30 .handle("update", ({ path, payload }) =>30 .handle("update", ({ path, payload }) =>
@@ -32,9 +32,7 @@ export const ExecutorPoliciesLive = HttpApiBuilder.group( · 1 unmodified line
32 "policies.update",32 "policies.update",
33 path.workspaceId,33 path.workspaceId,
34 ).pipe(34 ).pipe(
35 Effect.flatMap((executor) =>35 Effect.flatMap((executor) => executor.policies.update(path.policyId, payload)),
36 executor.effect.policies.update(path.policyId, payload)
37 ),
38 ),36 ),
39 )37 )
40 .handle("remove", ({ path }) =>38 .handle("remove", ({ path }) =>
@@ -42,10 +40,8 @@ export const ExecutorPoliciesLive = HttpApiBuilder.group( · 1 unmodified line
42 "policies.remove",40 "policies.remove",
43 path.workspaceId,41 path.workspaceId,
44 ).pipe(42 ).pipe(
45 Effect.flatMap((executor) => executor.effect.policies.remove(path.policyId)),43 Effect.flatMap((executor) => executor.policies.remove(path.policyId)),
46 Effect.map((result) => ({44 Effect.map((removed) => ({ removed })),
47 removed: result.removed,
48 })),
49 ),45 ),
50 ),46 ),
51);47);
packages/platform/api/src/sources/http.ts+22−26
@@ -713,7 +713,7 @@ const credentialSubmitErrorResponse = (input: { · 712 unmodified lines
713 input.workspaceId,713 input.workspaceId,
714 ).pipe(714 ).pipe(
715 Effect.flatMap((executor) =>715 Effect.flatMap((executor) =>
716 executor.effect.local.credentials.get({716 executor.local.credentials.get({
717 sourceId: input.sourceId,717 sourceId: input.sourceId,
718 interactionId: input.interactionId,718 interactionId: input.interactionId,
719 }),719 }),
@@ -749,7 +749,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 29 unmodified lines
749 handlers749 handlers
750 .handle("discover", ({ payload }) =>750 .handle("discover", ({ payload }) =>
751 Effect.flatMap(getControlPlaneExecutor(), (executor) =>751 Effect.flatMap(getControlPlaneExecutor(), (executor) =>
752 executor.effect.sources.discover({752 executor.sources.discover({
753 url: payload.url,753 url: payload.url,
754 probeAuth: payload.probeAuth,754 probeAuth: payload.probeAuth,
755 }).pipe(755 }).pipe(
@@ -770,14 +770,14 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 14 unmodified lines
770 const baseUrl = resolveRequestOrigin(request);770 const baseUrl = resolveRequestOrigin(request);
771 771
772 if (isInteractiveConnectPayload(payload)) {772 if (isInteractiveConnectPayload(payload)) {
773 return yield* executor.effect.sources.connect(773 return yield* executor.sources.connect(
774 toExecutorMcpSourceInput(payload, baseUrl),774 toExecutorMcpSourceInput(payload, baseUrl),
775 );775 );
776 }776 }
777 777
778 const sourceInput = toExecutorSourceInput(payload);778 const sourceInput = toExecutorSourceInput(payload);
779 if (sourceInput) {779 if (sourceInput) {
780 return yield* executor.effect.sources.add(sourceInput, {780 return yield* executor.sources.add(sourceInput, {
781 baseUrl,781 baseUrl,
782 });782 });
783 }783 }
@@ -804,7 +804,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 20 unmodified lines
804 Effect.flatMap((executor) =>804 Effect.flatMap((executor) =>
805 Effect.gen(function* () {805 Effect.gen(function* () {
806 const request = yield* HttpServerRequest.HttpServerRequest;806 const request = yield* HttpServerRequest.HttpServerRequest;
807 return yield* executor.effect.sources.connectBatch({807 return yield* executor.sources.connectBatch({
808 ...payload,808 ...payload,
809 baseUrl: resolveRequestOrigin(request),809 baseUrl: resolveRequestOrigin(request),
810 });810 });
@@ -822,7 +822,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 11 unmodified lines
822 path.workspaceId,822 path.workspaceId,
823 ).pipe(823 ).pipe(
824 Effect.flatMap((executor) =>824 Effect.flatMap((executor) =>
825 executor.effect.sources.oauthClients.list(urlParams.providerKey).pipe(825 executor.sources.oauthClients.list(urlParams.providerKey).pipe(
826 Effect.catchAll((cause) =>826 Effect.catchAll((cause) =>
827 Effect.fail(827 Effect.fail(
828 toBadRequestError("sources.listWorkspaceOauthClients", cause),828 toBadRequestError("sources.listWorkspaceOauthClients", cause),
@@ -838,7 +838,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 9 unmodified lines
838 path.workspaceId,838 path.workspaceId,
839 ).pipe(839 ).pipe(
840 Effect.flatMap((executor) =>840 Effect.flatMap((executor) =>
841 executor.effect.sources.oauthClients.create(payload).pipe(841 executor.sources.oauthClients.create(payload).pipe(
842 Effect.catchAll((cause) =>842 Effect.catchAll((cause) =>
843 Effect.fail(843 Effect.fail(
844 toBadRequestError(844 toBadRequestError(
@@ -857,7 +857,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 12 unmodified lines
857 path.workspaceId,857 path.workspaceId,
858 ).pipe(858 ).pipe(
859 Effect.flatMap((executor) =>859 Effect.flatMap((executor) =>
860 executor.effect.sources.oauthClients.remove(path.oauthClientId).pipe(860 executor.sources.oauthClients.remove(path.oauthClientId).pipe(
861 Effect.map((result) => ({ removed: result })),861 Effect.map((result) => ({ removed: result })),
862 Effect.catchAll((cause) =>862 Effect.catchAll((cause) =>
863 Effect.fail(863 Effect.fail(
@@ -877,7 +877,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 13 unmodified lines
877 path.workspaceId,877 path.workspaceId,
878 ).pipe(878 ).pipe(
879 Effect.flatMap((executor) =>879 Effect.flatMap((executor) =>
880 executor.effect.sources.providerGrants.remove(path.grantId).pipe(880 executor.sources.providerGrants.remove(path.grantId).pipe(
881 Effect.map((result) => ({ removed: result })),881 Effect.map((result) => ({ removed: result })),
882 Effect.catchAll((cause) =>882 Effect.catchAll((cause) =>
883 Effect.fail(883 Effect.fail(
@@ -893,17 +893,17 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 9 unmodified lines
893 )893 )
894 .handle("list", ({ path }) =>894 .handle("list", ({ path }) =>
895 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(895 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(
896 Effect.flatMap((executor) => executor.effect.sources.list()),896 Effect.flatMap((executor) => executor.sources.list()),
897 ),897 ),
898 )898 )
899 .handle("create", ({ path, payload }) =>899 .handle("create", ({ path, payload }) =>
900 resolveRequestedLocalWorkspace("sources.create", path.workspaceId).pipe(900 resolveRequestedLocalWorkspace("sources.create", path.workspaceId).pipe(
901 Effect.flatMap((executor) => executor.effect.sources.create(payload)),901 Effect.flatMap((executor) => executor.sources.create(payload)),
902 ),902 ),
903 )903 )
904 .handle("get", ({ path }) =>904 .handle("get", ({ path }) =>
905 resolveRequestedLocalWorkspace("sources.get", path.workspaceId).pipe(905 resolveRequestedLocalWorkspace("sources.get", path.workspaceId).pipe(
906 Effect.flatMap((executor) => executor.effect.sources.get(path.sourceId)),906 Effect.flatMap((executor) => executor.sources.get(path.sourceId)),
907 ),907 ),
908 )908 )
909 .handle("inspection", ({ path }) =>909 .handle("inspection", ({ path }) =>
@@ -911,9 +911,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 1 unmodified line
911 "sources.inspection",911 "sources.inspection",
912 path.workspaceId,912 path.workspaceId,
913 ).pipe(913 ).pipe(
914 Effect.flatMap((executor) =>914 Effect.flatMap((executor) => executor.sources.inspection.get(path.sourceId)),
915 executor.effect.sources.inspection.get(path.sourceId),
916 ),
917 ),915 ),
918 )916 )
919 .handle("inspectionTool", ({ path }) =>917 .handle("inspectionTool", ({ path }) =>
@@ -922,7 +920,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
922 path.workspaceId,920 path.workspaceId,
923 ).pipe(921 ).pipe(
924 Effect.flatMap((executor) =>922 Effect.flatMap((executor) =>
925 executor.effect.sources.inspection.tool({923 executor.sources.inspection.tool({
926 sourceId: path.sourceId,924 sourceId: path.sourceId,
927 toolPath: path.toolPath,925 toolPath: path.toolPath,
928 }),926 }),
@@ -935,7 +933,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 6 unmodified lines
935 path.workspaceId,933 path.workspaceId,
936 ).pipe(934 ).pipe(
937 Effect.flatMap((executor) =>935 Effect.flatMap((executor) =>
938 executor.effect.sources.inspection.discover({936 executor.sources.inspection.discover({
939 sourceId: path.sourceId,937 sourceId: path.sourceId,
940 payload,938 payload,
941 }),939 }),
@@ -944,15 +942,13 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
944 )942 )
945 .handle("update", ({ path, payload }) =>943 .handle("update", ({ path, payload }) =>
946 resolveRequestedLocalWorkspace("sources.update", path.workspaceId).pipe(944 resolveRequestedLocalWorkspace("sources.update", path.workspaceId).pipe(
947 Effect.flatMap((executor) =>945 Effect.flatMap((executor) => executor.sources.update(path.sourceId, payload)),
948 executor.effect.sources.update(path.sourceId, payload)
949 ),
950 ),946 ),
951 )947 )
952 .handle("remove", ({ path }) =>948 .handle("remove", ({ path }) =>
953 resolveRequestedLocalWorkspace("sources.remove", path.workspaceId).pipe(949 resolveRequestedLocalWorkspace("sources.remove", path.workspaceId).pipe(
954 Effect.flatMap((executor) => executor.effect.sources.remove(path.sourceId)),950 Effect.flatMap((executor) => executor.sources.remove(path.sourceId)),
955 Effect.map((result) => ({ removed: result.removed })),951 Effect.map((removed) => ({ removed })),
956 ),952 ),
957 )953 )
958 .handle("credentialPage", ({ path, urlParams }) =>954 .handle("credentialPage", ({ path, urlParams }) =>
@@ -961,7 +957,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 2 unmodified lines
961 path.workspaceId,957 path.workspaceId,
962 ).pipe(958 ).pipe(
963 Effect.flatMap((executor) =>959 Effect.flatMap((executor) =>
964 executor.effect.local.credentials.get({960 executor.local.credentials.get({
965 sourceId: path.sourceId,961 sourceId: path.sourceId,
966 interactionId: urlParams.interactionId,962 interactionId: urlParams.interactionId,
967 }),963 }),
@@ -1006,7 +1002,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 38 unmodified lines
1006 path.workspaceId,1002 path.workspaceId,
1007 ).pipe(1003 ).pipe(
1008 Effect.flatMap((executor) =>1004 Effect.flatMap((executor) =>
1009 executor.effect.local.credentials.submit({1005 executor.local.credentials.submit({
1010 sourceId: path.sourceId,1006 sourceId: path.sourceId,
1011 interactionId: urlParams.interactionId,1007 interactionId: urlParams.interactionId,
1012 action:1008 action:
@@ -1081,7 +1077,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 68 unmodified lines
1081 path.workspaceId,1077 path.workspaceId,
1082 ).pipe(1078 ).pipe(
1083 Effect.flatMap((executor) =>1079 Effect.flatMap((executor) =>
1084 executor.effect.local.credentials.complete({1080 executor.local.credentials.complete({
1085 sourceId: path.sourceId,1081 sourceId: path.sourceId,
1086 state: urlParams.state,1082 state: urlParams.state,
1087 code: urlParams.code,1083 code: urlParams.code,
@@ -1136,7 +1132,7 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 48 unmodified lines
1136 ).pipe(1132 ).pipe(
1137 Effect.flatMap((executor) =>1133 Effect.flatMap((executor) =>
1138 Effect.gen(function* () {1134 Effect.gen(function* () {
1139 const completed = yield* executor.effect.oauth.completeProviderCallback({1135 const completed = yield* executor.oauth.completeProviderCallback({
1140 scopeId: path.workspaceId,1136 scopeId: path.workspaceId,
1141 state: urlParams.state,1137 state: urlParams.state,
1142 code: urlParams.code,1138 code: urlParams.code,
packages/platform/sdk-file/package.json+3−1
@@ -3,7 +3,9 @@ · 2 unmodified lines
3 "private": true,3 "private": true,
4 "type": "module",4 "type": "module",
5 "exports": {5 "exports": {
6 ".": "./src/index.ts"6 ".": "./src/client.ts",
7 "./effect": "./src/effect.ts",
8 "./runtime": "./src/runtime.ts"
7 },9 },
8 "scripts": {10 "scripts": {
9 "fixture:release:capture": "bun run ./src/capture-release-workspace-fixture.ts",11 "fixture:release:capture": "bun run ./src/capture-release-workspace-fixture.ts",
packages/platform/sdk-file/src/client.ts+5−0
@@ -0,0 +1,5 @@
1export type { CreateLocalExecutorBackendOptions } from "./index";
2export {
3 createLocalExecutor,
4 createLocalExecutorBackend,
5} from "./index";
packages/platform/sdk-file/src/effect.ts+28−0
@@ -0,0 +1,28 @@
1export type { CreateLocalExecutorBackendOptions } from "./index";
2export {
3 buildLocalSourceArtifact,
4 createLocalExecutorEffect,
5 createLocalExecutorServicesEffect,
6 deriveLocalInstallation,
7 getOrProvisionLocalInstallation,
8 loadLocalExecutorConfig,
9 loadLocalExecutorStateSnapshot,
10 loadLocalInstallation,
11 loadLocalWorkspaceState,
12 readLocalSourceArtifact,
13 refreshSourceTypeDeclarationInBackground,
14 refreshWorkspaceSourceTypeDeclarationsInBackground,
15 removeLocalSourceArtifact,
16 resolveConfigRelativePath,
17 resolveLocalWorkspaceContext,
18 syncSourceTypeDeclarationNode,
19 syncWorkspaceSourceTypeDeclarationsNode,
20 writeLocalExecutorStateSnapshot,
21 writeLocalSourceArtifact,
22 writeLocalWorkspaceState,
23 writeProjectLocalExecutorConfig,
24} from "./index";
25export type {
26 LoadedLocalExecutorConfig,
27 ResolvedLocalWorkspaceContext,
28} from "./index";
packages/platform/sdk-file/src/index.ts+14−4
@@ -3,8 +3,6 @@ import { NodeFileSystem } from "@effect/platform-node"; · 2 unmodified lines
3import {3import {
4 createExecutor,4 createExecutor,
5 createExecutorBackend,5 createExecutorBackend,
6 createExecutorEffect,
7 type CreateExecutorOptions,
8 type Executor,6 type Executor,
9 type ExecutorBackend,7 type ExecutorBackend,
10 type ExecutorBackendServices,8 type ExecutorBackendServices,
@@ -12,6 +10,11 @@ import { · 1 unmodified line
12 type ExecutorSourceTypeDeclarationsBackend,10 type ExecutorSourceTypeDeclarationsBackend,
13 type ExecutorScopeDescriptor,11 type ExecutorScopeDescriptor,
14} from "@executor/platform-sdk";12} from "@executor/platform-sdk";
13import {
14 createExecutorEffect,
15 type ExecutorEffect,
16 type CreateExecutorEffectOptions as CreateExecutorOptions,
17} from "@executor/platform-sdk/effect";
15import type {18import type {
16 LocalExecutorConfig,19 LocalExecutorConfig,
17 LocalInstallation,20 LocalInstallation,
@@ -307,7 +310,7 @@ export const createLocalExecutorRuntime = ( · 289 unmodified lines
307 310
308export const createLocalExecutorEffect = (311export const createLocalExecutorEffect = (
309 options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {},312 options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {},
310): Effect.Effect<Executor, Error> =>313): Effect.Effect<ExecutorEffect, Error> =>
311 createExecutorEffect({314 createExecutorEffect({
312 backend: createLocalExecutorBackend(options),315 backend: createLocalExecutorBackend(options),
313 executionResolver: options.executionResolver,316 executionResolver: options.executionResolver,
@@ -318,7 +321,14 @@ export const createLocalExecutorEffect = ( · 4 unmodified lines
318 321
319export const createLocalExecutor = (322export const createLocalExecutor = (
320 options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {},323 options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {},
321): Promise<Executor> => Effect.runPromise(createLocalExecutorEffect(options));324): Promise<Executor> =>
325 createExecutor({
326 backend: createLocalExecutorBackend(options),
327 executionResolver: options.executionResolver,
328 createInternalToolMap: options.createInternalToolMap,
329 resolveSecretMaterial: options.resolveSecretMaterial,
330 getLocalServerBaseUrl: options.getLocalServerBaseUrl,
331 });
322 332
323export {333export {
324 deriveLocalInstallation,334 deriveLocalInstallation,
packages/platform/sdk-file/src/runtime.ts+2−0
@@ -0,0 +1,2 @@
1export type { CreateLocalExecutorBackendOptions } from "./index";
2export { createLocalExecutorRuntime } from "./index";
packages/platform/sdk/package.json+1−0
@@ -6,6 +6,7 @@ · 5 unmodified lines
6 ".": "./src/index.ts",6 ".": "./src/index.ts",
7 "./backend": "./src/backend.ts",7 "./backend": "./src/backend.ts",
8 "./contracts": "./src/contracts.ts",8 "./contracts": "./src/contracts.ts",
9 "./effect": "./src/effect.ts",
9 "./executor": "./src/executor.ts",10 "./executor": "./src/executor.ts",
10 "./errors": "./src/errors.ts",11 "./errors": "./src/errors.ts",
11 "./operations": "./src/operations.ts",12 "./operations": "./src/operations.ts",
packages/platform/sdk/src/effect.ts+1−0
@@ -0,0 +1 @@
1export * from "./executor-effect";
packages/platform/sdk/src/executor-effect.ts+533−0
@@ -0,0 +1,533 @@
1import * as Effect from "effect/Effect";
2
3import type {
4 ScopeId,
5 Execution,
6 ExecutionEnvelope,
7 ExecutionInteraction,
8 LocalInstallation,
9 LocalScopePolicy,
10 ProviderAuthGrant,
11 ScopeOauthClient,
12 Source,
13} from "./schema";
14import { ExecutionIdSchema } from "./schema";
15import type {
16 CreateExecutionPayload,
17 ResumeExecutionPayload,
18} from "./executions/contracts";
19import type {
20 CreateSecretPayload,
21 CreateSecretResult,
22 DeleteSecretResult,
23 InstanceConfig,
24 SecretListItem,
25 UpdateSecretPayload,
26 UpdateSecretResult,
27} from "./local/contracts";
28import {
29 completeSourceCredentialSetup,
30 getLocalInstallation,
31 getSourceCredentialInteraction,
32 submitSourceCredentialInteraction,
33} from "./local/operations";
34import {
35 createLocalSecret,
36 deleteLocalSecret,
37 getLocalInstanceConfig,
38 listLocalSecrets,
39 updateLocalSecret,
40} from "./local/secrets";
41import type {
42 CreatePolicyPayload,
43 UpdatePolicyPayload,
44} from "./policies/contracts";
45import {
46 createPolicy,
47 getPolicy,
48 listPolicies,
49 removePolicy,
50 updatePolicy,
51} from "./policies/operations";
52import type {
53 CreateScopeOauthClientPayload,
54 CreateSourcePayload,
55 UpdateSourcePayload,
56} from "./sources/contracts";
57import { discoverSource } from "./sources/discovery";
58import {
59 discoverSourceInspectionTools,
60 getSourceInspection,
61 getSourceInspectionToolDetail,
62} from "./sources/inspection";
63import {
64 createSource,
65 getSource,
66 listSources,
67 removeSource,
68 updateSource,
69} from "./sources/operations";
70import type { ExecutorBackend } from "./backend";
71import {
72 provideExecutorRuntime,
73 type CreateScopeInternalToolMap,
74 type ExecutorRuntime,
75 type ExecutorRuntimeOptions,
76 type ResolveExecutionEnvironment,
77 type ResolveSecretMaterial,
78 RuntimeSourceAuthServiceTag,
79} from "./runtime";
80import {
81 createExecution,
82 getExecution,
83 resumeExecution,
84} from "./runtime/execution/service";
85import type {
86 CompleteProviderOauthCallbackResult,
87 CompleteSourceCredentialSetupResult,
88 CompleteSourceOAuthSessionResult,
89 ConnectGoogleDiscoveryBatchInput,
90 ConnectGoogleDiscoveryBatchResult,
91 ConnectMcpSourceInput,
92 ExecutorAddSourceInput,
93 ExecutorSourceAddResult,
94 McpSourceConnectResult,
95 StartSourceOAuthSessionInput,
96 StartSourceOAuthSessionResult,
97} from "./runtime/sources/source-auth-service";
98
99type DistributiveOmit<T, Keys extends PropertyKey> = T extends unknown
100 ? Omit<T, Keys>
101 : never;
102type ProvidedEffect<T extends Effect.Effect<any, any, any>> = Effect.Effect<
103 Effect.Effect.Success<T>,
104 Effect.Effect.Error<T>,
105 never
106>;
107type MappedProvidedEffect<
108 T extends Effect.Effect<any, any, any>,
109 A,
110> = Effect.Effect<A, Effect.Effect.Error<T>, never>;
111
112export type ExecutorSourceInput = DistributiveOmit<
113 ExecutorAddSourceInput,
114 "scopeId" | "actorScopeId" | "executionId" | "interactionId"
115>;
116
117export type ExecutorSourceBatchInput = DistributiveOmit<
118 ConnectGoogleDiscoveryBatchInput,
119 "scopeId" | "actorScopeId" | "executionId" | "interactionId"
120>;
121
122export type ExecutorMcpSourceInput = DistributiveOmit<
123 ConnectMcpSourceInput,
124 "scopeId" | "actorScopeId"
125>;
126
127export type ExecutorSourceOAuthInput = DistributiveOmit<
128 StartSourceOAuthSessionInput,
129 "scopeId" | "actorScopeId"
130>;
131
132export type ExecutorEffect = {
133 runtime: ExecutorRuntime;
134 installation: LocalInstallation;
135 scopeId: ScopeId;
136 actorScopeId: ScopeId;
137 resolutionScopeIds: ReadonlyArray<ScopeId>;
138 close: () => Promise<void>;
139 local: {
140 installation: () => ProvidedEffect<ReturnType<typeof getLocalInstallation>>;
141 config: () => ProvidedEffect<ReturnType<typeof getLocalInstanceConfig>>;
142 credentials: {
143 get: (input: {
144 sourceId: Source["id"];
145 interactionId: ExecutionInteraction["id"];
146 }) => ProvidedEffect<ReturnType<typeof getSourceCredentialInteraction>>;
147 submit: (input: {
148 sourceId: Source["id"];
149 interactionId: ExecutionInteraction["id"];
150 action: "submit" | "continue" | "cancel";
151 token?: string | null;
152 }) => ProvidedEffect<
153 ReturnType<typeof submitSourceCredentialInteraction>
154 >;
155 complete: (input: {
156 sourceId: Source["id"];
157 state: string;
158 code?: string | null;
159 error?: string | null;
160 errorDescription?: string | null;
161 }) => MappedProvidedEffect<
162 ReturnType<typeof completeSourceCredentialSetup>,
163 CompleteSourceCredentialSetupResult
164 >;
165 };
166 };
167 secrets: {
168 list: () => ProvidedEffect<ReturnType<typeof listLocalSecrets>>;
169 create: (
170 payload: CreateSecretPayload,
171 ) => ProvidedEffect<ReturnType<typeof createLocalSecret>>;
172 update: (input: {
173 secretId: string;
174 payload: UpdateSecretPayload;
175 }) => ProvidedEffect<ReturnType<typeof updateLocalSecret>>;
176 remove: (
177 secretId: string,
178 ) => MappedProvidedEffect<
179 ReturnType<typeof deleteLocalSecret>,
180 DeleteSecretResult
181 >;
182 };
183 policies: {
184 list: () => ProvidedEffect<ReturnType<typeof listPolicies>>;
185 create: (
186 payload: CreatePolicyPayload,
187 ) => ProvidedEffect<ReturnType<typeof createPolicy>>;
188 get: (policyId: string) => ProvidedEffect<ReturnType<typeof getPolicy>>;
189 update: (
190 policyId: string,
191 payload: UpdatePolicyPayload,
192 ) => ProvidedEffect<ReturnType<typeof updatePolicy>>;
193 remove: (
194 policyId: string,
195 ) => MappedProvidedEffect<ReturnType<typeof removePolicy>, boolean>;
196 };
197 sources: {
198 add: (
199 input: ExecutorSourceInput,
200 options?: {
201 baseUrl?: string | null;
202 },
203 ) => Effect.Effect<ExecutorSourceAddResult, Error, never>;
204 connect: (
205 payload: ExecutorMcpSourceInput,
206 ) => Effect.Effect<McpSourceConnectResult, Error, never>;
207 connectBatch: (
208 payload: ExecutorSourceBatchInput,
209 ) => Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, never>;
210 discover: (input: {
211 url: string;
212 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
213 }) => ProvidedEffect<ReturnType<typeof discoverSource>>;
214 list: () => ProvidedEffect<ReturnType<typeof listSources>>;
215 create: (
216 payload: CreateSourcePayload,
217 ) => ProvidedEffect<ReturnType<typeof createSource>>;
218 get: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof getSource>>;
219 update: (
220 sourceId: Source["id"],
221 payload: UpdateSourcePayload,
222 ) => ProvidedEffect<ReturnType<typeof updateSource>>;
223 remove: (
224 sourceId: Source["id"],
225 ) => MappedProvidedEffect<ReturnType<typeof removeSource>, boolean>;
226 inspection: {
227 get: (
228 sourceId: Source["id"],
229 ) => ProvidedEffect<ReturnType<typeof getSourceInspection>>;
230 tool: (input: {
231 sourceId: Source["id"];
232 toolPath: string;
233 }) => ProvidedEffect<ReturnType<typeof getSourceInspectionToolDetail>>;
234 discover: (input: {
235 sourceId: Source["id"];
236 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
237 }) => ProvidedEffect<
238 ReturnType<typeof discoverSourceInspectionTools>
239 >;
240 };
241 oauthClients: {
242 list: (
243 providerKey: string,
244 ) => Effect.Effect<ReadonlyArray<ScopeOauthClient>, Error, never>;
245 create: (
246 payload: CreateScopeOauthClientPayload,
247 ) => Effect.Effect<ScopeOauthClient, Error, never>;
248 remove: (
249 oauthClientId: ScopeOauthClient["id"],
250 ) => Effect.Effect<boolean, Error, never>;
251 };
252 providerGrants: {
253 remove: (
254 grantId: ProviderAuthGrant["id"],
255 ) => Effect.Effect<boolean, Error, never>;
256 };
257 };
258 oauth: {
259 startSourceAuth: (
260 input: ExecutorSourceOAuthInput,
261 ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
262 completeSourceAuth: (input: {
263 state: string;
264 code?: string | null;
265 error?: string | null;
266 errorDescription?: string | null;
267 }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>;
268 completeProviderCallback: (input: {
269 scopeId?: ScopeId;
270 actorScopeId?: ScopeId | null;
271 state: string;
272 code?: string | null;
273 error?: string | null;
274 errorDescription?: string | null;
275 }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, never>;
276 };
277 executions: {
278 create: (
279 payload: CreateExecutionPayload,
280 ) => ProvidedEffect<ReturnType<typeof createExecution>>;
281 get: (
282 executionId: Execution["id"],
283 ) => ProvidedEffect<ReturnType<typeof getExecution>>;
284 resume: (
285 executionId: Execution["id"],
286 payload: ResumeExecutionPayload,
287 ) => ProvidedEffect<ReturnType<typeof resumeExecution>>;
288 };
289};
290
291export type CreateExecutorEffectOptions = ExecutorRuntimeOptions & {
292 backend: ExecutorBackend;
293};
294
295const fromRuntime = (runtime: ExecutorRuntime): ExecutorEffect => {
296 const installation = runtime.localInstallation;
297 const scopeId = installation.scopeId;
298 const actorScopeId = installation.actorScopeId;
299 const provide = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
300 provideExecutorRuntime(effect, runtime);
301 const provideSourceAuth = <A, E>(
302 execute: (
303 service: Effect.Effect.Success<typeof RuntimeSourceAuthServiceTag>,
304 ) => Effect.Effect<A, E, any>,
305 ) => provide(Effect.flatMap(RuntimeSourceAuthServiceTag, execute));
306 const createSdkSourceSession = () => {
307 const id = crypto.randomUUID();
308 return {
309 executionId: ExecutionIdSchema.make(`exec_sdk_${id}`),
310 interactionId: `executor.sdk.${id}` as never,
311 };
312 };
313
314 return {
315 runtime,
316 installation,
317 scopeId,
318 actorScopeId,
319 resolutionScopeIds: installation.resolutionScopeIds,
320 close: () => runtime.close(),
321 local: {
322 installation: () => provide(getLocalInstallation()),
323 config: () => provide(getLocalInstanceConfig()),
324 credentials: {
325 get: ({ sourceId, interactionId }) =>
326 provide(
327 getSourceCredentialInteraction({
328 scopeId,
329 sourceId,
330 interactionId,
331 }),
332 ),
333 submit: ({ sourceId, interactionId, action, token }) =>
334 provide(
335 submitSourceCredentialInteraction({
336 scopeId,
337 sourceId,
338 interactionId,
339 action,
340 token,
341 }),
342 ),
343 complete: ({ sourceId, state, code, error, errorDescription }) =>
344 provide(
345 completeSourceCredentialSetup({
346 scopeId,
347 sourceId,
348 state,
349 code,
350 error,
351 errorDescription,
352 }),
353 ),
354 },
355 },
356 secrets: {
357 list: () => provide(listLocalSecrets()),
358 create: (payload) => provide(createLocalSecret(payload)),
359 update: (input) => provide(updateLocalSecret(input)),
360 remove: (secretId) => provide(deleteLocalSecret(secretId)),
361 },
362 policies: {
363 list: () => provide(listPolicies(scopeId)),
364 create: (payload) => provide(createPolicy({ scopeId, payload })),
365 get: (policyId) =>
366 provide(getPolicy({ scopeId, policyId: policyId as never })),
367 update: (policyId, payload) =>
368 provide(updatePolicy({ scopeId, policyId: policyId as never, payload })),
369 remove: (policyId) =>
370 provide(removePolicy({ scopeId, policyId: policyId as never })).pipe(
371 Effect.map((result) => result.removed),
372 ),
373 },
374 sources: {
375 add: (input, options) =>
376 provideSourceAuth((service) => {
377 const session = createSdkSourceSession();
378 return service.addExecutorSource(
379 {
380 ...input,
381 scopeId,
382 actorScopeId,
383 executionId: session.executionId,
384 interactionId: session.interactionId,
385 },
386 options,
387 );
388 }),
389 connect: (payload) =>
390 provideSourceAuth((service) =>
391 service.connectMcpSource({
392 ...payload,
393 scopeId,
394 actorScopeId,
395 }),
396 ),
397 connectBatch: (payload) =>
398 provideSourceAuth((service) => {
399 const session = createSdkSourceSession();
400 return service.connectGoogleDiscoveryBatch({
401 ...payload,
402 scopeId,
403 actorScopeId,
404 executionId: session.executionId,
405 interactionId: session.interactionId,
406 });
407 }),
408 discover: (input) => provide(discoverSource(input)),
409 list: () => provide(listSources({ scopeId, actorScopeId })),
410 create: (payload) => provide(createSource({ scopeId, actorScopeId, payload })),
411 get: (sourceId) => provide(getSource({ scopeId, sourceId, actorScopeId })),
412 update: (sourceId, payload) =>
413 provide(updateSource({ scopeId, sourceId, actorScopeId, payload })),
414 remove: (sourceId) =>
415 provide(removeSource({ scopeId, sourceId })).pipe(
416 Effect.map((result) => result.removed),
417 ),
418 inspection: {
419 get: (sourceId) => provide(getSourceInspection({ scopeId, sourceId })),
420 tool: ({ sourceId, toolPath }) =>
421 provide(
422 getSourceInspectionToolDetail({
423 scopeId,
424 sourceId,
425 toolPath,
426 }),
427 ),
428 discover: ({ sourceId, payload }) =>
429 provide(
430 discoverSourceInspectionTools({
431 scopeId,
432 sourceId,
433 payload,
434 }),
435 ),
436 },
437 oauthClients: {
438 list: (providerKey) =>
439 provideSourceAuth((service) =>
440 service.listScopeOauthClients({
441 scopeId,
442 providerKey,
443 }),
444 ),
445 create: (payload) =>
446 provideSourceAuth((service) =>
447 service.createScopeOauthClient({
448 scopeId,
449 providerKey: payload.providerKey,
450 label: payload.label,
451 oauthClient: payload.oauthClient,
452 }),
453 ),
454 remove: (oauthClientId) =>
455 provideSourceAuth((service) =>
456 service.removeScopeOauthClient({
457 scopeId,
458 oauthClientId,
459 }),
460 ),
461 },
462 providerGrants: {
463 remove: (grantId) =>
464 provideSourceAuth((service) =>
465 service.removeProviderAuthGrant({
466 scopeId,
467 grantId,
468 }),
469 ),
470 },
471 },
472 oauth: {
473 startSourceAuth: (input) =>
474 provideSourceAuth((service) =>
475 service.startSourceOAuthSession({
476 ...input,
477 scopeId,
478 actorScopeId,
479 }),
480 ),
481 completeSourceAuth: ({ state, code, error, errorDescription }) =>
482 provideSourceAuth((service) =>
483 service.completeSourceOAuthSession({
484 state,
485 code,
486 error,
487 errorDescription,
488 }),
489 ),
490 completeProviderCallback: (input) =>
491 provideSourceAuth((service) =>
492 service.completeProviderOauthCallback({
493 ...input,
494 scopeId: input.scopeId ?? scopeId,
495 actorScopeId: input.actorScopeId ?? actorScopeId,
496 }),
497 ),
498 },
499 executions: {
500 create: (payload) =>
501 provide(
502 createExecution({
503 scopeId,
504 payload,
505 createdByScopeId: actorScopeId,
506 }),
507 ),
508 get: (executionId) => provide(getExecution({ scopeId, executionId })),
509 resume: (executionId, payload) =>
510 provide(
511 resumeExecution({
512 scopeId,
513 executionId,
514 payload,
515 resumedByScopeId: actorScopeId,
516 }),
517 ),
518 },
519 };
520};
521
522export const createExecutorEffect = (
523 options: CreateExecutorEffectOptions,
524): Effect.Effect<ExecutorEffect, Error> =>
525 Effect.map(
526 options.backend.createRuntime({
527 executionResolver: options.executionResolver,
528 createInternalToolMap: options.createInternalToolMap,
529 resolveSecretMaterial: options.resolveSecretMaterial,
530 getLocalServerBaseUrl: options.getLocalServerBaseUrl,
531 }),
532 fromRuntime,
533 );
packages/platform/sdk/src/executor.test.ts+8−4
@@ -30,19 +30,23 @@ import { · 29 unmodified lines
30 30
31import {31import {
32 createExecutorBackend,32 createExecutorBackend,
33 createExecutorEffect,
34 type Executor,
35} from "./index";33} from "./index";
34import {
35 createExecutorEffect,
36 type ExecutorEffect as Executor,
37} from "./effect";
36import {38import {
37 type LocalInstallation,39 type LocalInstallation,
38 ScopeIdSchema,40 ScopeIdSchema,
39} from "./schema";41} from "./schema";
40import {42import {
41 createLocalExecutorServicesEffect,43 createLocalExecutorServicesEffect,
42} from "../../sdk-file/src/index";44} from "../../sdk-file/src/effect";
43import {45import {
44 ExecutorApi,
45 createExecutorApiLayer,46 createExecutorApiLayer,
47} from "../../api/src/http";
48import {
49 ExecutorApi,
46} from "../../api/src/index";50} from "../../api/src/index";
47 51
48type LocalExecutorServices = Effect.Effect.Success<52type LocalExecutorServices = Effect.Effect.Success<
packages/platform/sdk/src/executor.ts+70−664
@@ -1,705 +1,111 @@
1import * as Effect from "effect/Effect";1import * as Effect from "effect/Effect";
2 2
3import type {
4 ScopeId,
5 Execution,
6 ExecutionEnvelope,
7 ExecutionInteraction,
8 LocalInstallation,
9 LocalScopePolicy,
10 ProviderAuthGrant,
11 Source,
12 ScopeOauthClient,
13} from "./schema";
14import {3import {
15 ExecutionIdSchema,4 createExecutorEffect,
16} from "./schema";5 type CreateExecutorEffectOptions,
17import type {6 type ExecutorEffect,
18 CreateExecutionPayload,7 type ExecutorMcpSourceInput,
19 ResumeExecutionPayload,8 type ExecutorSourceBatchInput,
20} from "./executions/contracts";9 type ExecutorSourceInput,
21import type {10 type ExecutorSourceOAuthInput,
22 CreateSecretPayload,11} from "./executor-effect";
23 CreateSecretResult,
24 DeleteSecretResult,
25 InstanceConfig,
26 SecretListItem,
27 UpdateSecretPayload,
28 UpdateSecretResult,
29} from "./local/contracts";
30import {
31 completeSourceCredentialSetup,
32 getLocalInstallation,
33 getSourceCredentialInteraction,
34 submitSourceCredentialInteraction,
35} from "./local/operations";
36import {
37 createLocalSecret,
38 deleteLocalSecret,
39 getLocalInstanceConfig,
40 listLocalSecrets,
41 updateLocalSecret,
42} from "./local/secrets";
43import type {
44 CreatePolicyPayload,
45 UpdatePolicyPayload,
46} from "./policies/contracts";
47import {
48 createPolicy,
49 getPolicy,
50 listPolicies,
51 removePolicy,
52 updatePolicy,
53} from "./policies/operations";
54import type {
55 CreateSourcePayload,
56 CreateScopeOauthClientPayload,
57 UpdateSourcePayload,
58} from "./sources/contracts";
59import {
60 discoverSource,
61} from "./sources/discovery";
62import {
63 discoverSourceInspectionTools,
64 getSourceInspection,
65 getSourceInspectionToolDetail,
66} from "./sources/inspection";
67import {
68 createSource,
69 getSource,
70 listSources,
71 removeSource,
72 updateSource,
73} from "./sources/operations";
74import type {
75 ExecutorBackend,
76} from "./backend";
77import {
78 provideExecutorRuntime,
79 type ExecutorRuntime,
80 type ExecutorRuntimeOptions,
81 type CreateScopeInternalToolMap,
82 type ResolveExecutionEnvironment,
83 type ResolveSecretMaterial,
84 RuntimeSourceAuthServiceTag,
85} from "./runtime";
86import {
87 createExecution,
88 getExecution,
89 resumeExecution,
90} from "./runtime/execution/service";
91import type {
92 CompleteProviderOauthCallbackResult,
93 CompleteSourceCredentialSetupResult,
94 CompleteSourceOAuthSessionResult,
95 ConnectGoogleDiscoveryBatchInput,
96 ConnectGoogleDiscoveryBatchResult,
97 ConnectMcpSourceInput,
98 ExecutorAddSourceInput,
99 ExecutorSourceAddResult,
100 McpSourceConnectResult,
101 StartSourceOAuthSessionInput,
102 StartSourceOAuthSessionResult,
103} from "./runtime/sources/source-auth-service";
104
105type DistributiveOmit<T, Keys extends PropertyKey> = T extends unknown ? Omit<T, Keys> : never;
106type ProvidedEffect<T extends Effect.Effect<any, any, any>> = Effect.Effect<
107 Effect.Effect.Success<T>,
108 Effect.Effect.Error<T>,
109 never
110>;
111
112export type ExecutorSourceInput = DistributiveOmit<
113 ExecutorAddSourceInput,
114 "scopeId" | "actorScopeId" | "executionId" | "interactionId"
115>;
116
117export type ExecutorSourceBatchInput = DistributiveOmit<
118 ConnectGoogleDiscoveryBatchInput,
119 "scopeId" | "actorScopeId" | "executionId" | "interactionId"
120>;
121 12
122export type ExecutorMcpSourceInput = DistributiveOmit<13type Promiseify<T> = T extends Effect.Effect<infer A, any, any>
123 ConnectMcpSourceInput,14 ? Promise<A>
124 "scopeId" | "actorScopeId"15 : T extends (...args: infer Args) => infer Result
125>;16 ? (...args: Args) => Promiseify<Result>
126 17 : T extends Promise<infer A>
127export type ExecutorSourceOAuthInput = DistributiveOmit<18 ? Promise<A>
128 StartSourceOAuthSessionInput,19 : T extends object
129 "scopeId" | "actorScopeId"20 ? { [Key in keyof T]: Promiseify<T[Key]> }
130>;21 : T;
131
132export type Executor = {
133 runtime: ExecutorRuntime;
134 installation: LocalInstallation;
135 scopeId: ScopeId;
136 actorScopeId: ScopeId;
137 resolutionScopeIds: ReadonlyArray<ScopeId>;
138 provide: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, any>;
139 run: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
140 close: () => Promise<void>;
141 effect: {
142 local: {
143 installation: () => ProvidedEffect<ReturnType<typeof getLocalInstallation>>;
144 config: () => ProvidedEffect<ReturnType<typeof getLocalInstanceConfig>>;
145 credentials: {
146 get: (input: {
147 sourceId: Source["id"];
148 interactionId: ExecutionInteraction["id"];
149 }) => ProvidedEffect<ReturnType<typeof getSourceCredentialInteraction>>;
150 submit: (input: {
151 sourceId: Source["id"];
152 interactionId: ExecutionInteraction["id"];
153 action: "submit" | "continue" | "cancel";
154 token?: string | null;
155 }) => ProvidedEffect<ReturnType<typeof submitSourceCredentialInteraction>>;
156 complete: (input: {
157 sourceId: Source["id"];
158 state: string;
159 code?: string | null;
160 error?: string | null;
161 errorDescription?: string | null;
162 }) => ProvidedEffect<ReturnType<typeof completeSourceCredentialSetup>>;
163 };
164 };
165 secrets: {
166 list: () => ProvidedEffect<ReturnType<typeof listLocalSecrets>>;
167 create: (payload: CreateSecretPayload) => ProvidedEffect<ReturnType<typeof createLocalSecret>>;
168 update: (input: {
169 secretId: string;
170 payload: UpdateSecretPayload;
171 }) => ProvidedEffect<ReturnType<typeof updateLocalSecret>>;
172 remove: (secretId: string) => ProvidedEffect<ReturnType<typeof deleteLocalSecret>>;
173 };
174 policies: {
175 list: () => ProvidedEffect<ReturnType<typeof listPolicies>>;
176 create: (payload: CreatePolicyPayload) => ProvidedEffect<ReturnType<typeof createPolicy>>;
177 get: (policyId: string) => ProvidedEffect<ReturnType<typeof getPolicy>>;
178 update: (
179 policyId: string,
180 payload: UpdatePolicyPayload,
181 ) => ProvidedEffect<ReturnType<typeof updatePolicy>>;
182 remove: (policyId: string) => ProvidedEffect<ReturnType<typeof removePolicy>>;
183 };
184 sources: {
185 add: (
186 input: ExecutorSourceInput,
187 options?: {
188 baseUrl?: string | null;
189 },
190 ) => Effect.Effect<ExecutorSourceAddResult, Error, never>;
191 connect: (payload: ExecutorMcpSourceInput) => Effect.Effect<McpSourceConnectResult, Error, never>;
192 connectBatch: (
193 payload: ExecutorSourceBatchInput,
194 ) => Effect.Effect<ConnectGoogleDiscoveryBatchResult, Error, never>;
195 discover: (input: {
196 url: string;
197 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
198 }) => ProvidedEffect<ReturnType<typeof discoverSource>>;
199 list: () => ProvidedEffect<ReturnType<typeof listSources>>;
200 create: (payload: CreateSourcePayload) => ProvidedEffect<ReturnType<typeof createSource>>;
201 get: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof getSource>>;
202 update: (
203 sourceId: Source["id"],
204 payload: UpdateSourcePayload,
205 ) => ProvidedEffect<ReturnType<typeof updateSource>>;
206 remove: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof removeSource>>;
207 inspection: {
208 get: (sourceId: Source["id"]) => ProvidedEffect<ReturnType<typeof getSourceInspection>>;
209 tool: (input: {
210 sourceId: Source["id"];
211 toolPath: string;
212 }) => ProvidedEffect<ReturnType<typeof getSourceInspectionToolDetail>>;
213 discover: (input: {
214 sourceId: Source["id"];
215 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
216 }) => ProvidedEffect<ReturnType<typeof discoverSourceInspectionTools>>;
217 };
218 oauthClients: {
219 list: (
220 providerKey: string,
221 ) => Effect.Effect<ReadonlyArray<ScopeOauthClient>, Error, never>;
222 create: (
223 payload: CreateScopeOauthClientPayload,
224 ) => Effect.Effect<ScopeOauthClient, Error, never>;
225 remove: (
226 oauthClientId: ScopeOauthClient["id"],
227 ) => Effect.Effect<boolean, Error, never>;
228 };
229 providerGrants: {
230 remove: (grantId: ProviderAuthGrant["id"]) => Effect.Effect<boolean, Error, never>;
231 };
232 };
233 oauth: {
234 startSourceAuth: (
235 input: ExecutorSourceOAuthInput,
236 ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
237 completeSourceAuth: (input: {
238 state: string;
239 code?: string | null;
240 error?: string | null;
241 errorDescription?: string | null;
242 }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>;
243 completeProviderCallback: (input: {
244 scopeId?: ScopeId;
245 actorScopeId?: ScopeId | null;
246 state: string;
247 code?: string | null;
248 error?: string | null;
249 errorDescription?: string | null;
250 }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, never>;
251 };
252 executions: {
253 create: (payload: CreateExecutionPayload) => ProvidedEffect<ReturnType<typeof createExecution>>;
254 get: (executionId: Execution["id"]) => ProvidedEffect<ReturnType<typeof getExecution>>;
255 resume: (
256 executionId: Execution["id"],
257 payload: ResumeExecutionPayload,
258 ) => ProvidedEffect<ReturnType<typeof resumeExecution>>;
259 };
260 };
261 local: {
262 installation: () => Promise<LocalInstallation>;
263 config: () => Promise<InstanceConfig>;
264 credentials: {
265 get: (input: {
266 sourceId: Source["id"];
267 interactionId: ExecutionInteraction["id"];
268 }) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceCredentialInteraction>>>;
269 submit: (input: {
270 sourceId: Source["id"];
271 interactionId: ExecutionInteraction["id"];
272 action: "submit" | "continue" | "cancel";
273 token?: string | null;
274 }) => Promise<Effect.Effect.Success<ReturnType<typeof submitSourceCredentialInteraction>>>;
275 complete: (input: {
276 sourceId: Source["id"];
277 state: string;
278 code?: string | null;
279 error?: string | null;
280 errorDescription?: string | null;
281 }) => Promise<CompleteSourceCredentialSetupResult>;
282 };
283 };
284 secrets: {
285 list: () => Promise<ReadonlyArray<SecretListItem>>;
286 create: (payload: CreateSecretPayload) => Promise<CreateSecretResult>;
287 update: (input: {
288 secretId: string;
289 payload: UpdateSecretPayload;
290 }) => Promise<UpdateSecretResult>;
291 remove: (secretId: string) => Promise<DeleteSecretResult>;
292 };
293 policies: {
294 list: () => Promise<ReadonlyArray<LocalScopePolicy>>;
295 create: (payload: CreatePolicyPayload) => Promise<LocalScopePolicy>;
296 get: (policyId: string) => Promise<LocalScopePolicy>;
297 update: (
298 policyId: string,
299 payload: UpdatePolicyPayload,
300 ) => Promise<LocalScopePolicy>;
301 remove: (policyId: string) => Promise<boolean>;
302 };
303 sources: {
304 add: (
305 input: ExecutorSourceInput,
306 options?: {
307 baseUrl?: string | null;
308 },
309 ) => Promise<ExecutorSourceAddResult>;
310 connect: (payload: ExecutorMcpSourceInput) => Promise<McpSourceConnectResult>;
311 connectBatch: (payload: ExecutorSourceBatchInput) => Promise<ConnectGoogleDiscoveryBatchResult>;
312 discover: (input: {
313 url: string;
314 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
315 }) => Promise<Effect.Effect.Success<ReturnType<typeof discoverSource>>>;
316 list: () => Promise<ReadonlyArray<Source>>;
317 create: (payload: CreateSourcePayload) => Promise<Source>;
318 get: (sourceId: Source["id"]) => Promise<Source>;
319 update: (sourceId: Source["id"], payload: UpdateSourcePayload) => Promise<Source>;
320 remove: (sourceId: Source["id"]) => Promise<boolean>;
321 inspection: {
322 get: (sourceId: Source["id"]) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceInspection>>>;
323 tool: (input: {
324 sourceId: Source["id"];
325 toolPath: string;
326 }) => Promise<Effect.Effect.Success<ReturnType<typeof getSourceInspectionToolDetail>>>;
327 discover: (input: {
328 sourceId: Source["id"];
329 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
330 }) => Promise<Effect.Effect.Success<ReturnType<typeof discoverSourceInspectionTools>>>;
331 };
332 oauthClients: {
333 list: (providerKey: string) => Promise<ReadonlyArray<ScopeOauthClient>>;
334 create: (
335 payload: CreateScopeOauthClientPayload,
336 ) => Promise<ScopeOauthClient>;
337 remove: (oauthClientId: ScopeOauthClient["id"]) => Promise<boolean>;
338 };
339 providerGrants: {
340 remove: (grantId: ProviderAuthGrant["id"]) => Promise<boolean>;
341 };
342 };
343 oauth: {
344 startSourceAuth: (input: ExecutorSourceOAuthInput) => Promise<StartSourceOAuthSessionResult>;
345 completeSourceAuth: (input: {
346 state: string;
347 code?: string | null;
348 error?: string | null;
349 errorDescription?: string | null;
350 }) => Promise<CompleteSourceOAuthSessionResult>;
351 completeProviderCallback: (input: {
352 scopeId?: ScopeId;
353 actorScopeId?: ScopeId | null;
354 state: string;
355 code?: string | null;
356 error?: string | null;
357 errorDescription?: string | null;
358 }) => Promise<CompleteProviderOauthCallbackResult>;
359 };
360 executions: {
361 create: (payload: CreateExecutionPayload) => Promise<ExecutionEnvelope>;
362 get: (executionId: Execution["id"]) => Promise<ExecutionEnvelope>;
363 resume: (
364 executionId: Execution["id"],
365 payload: ResumeExecutionPayload,
366 ) => Promise<ExecutionEnvelope>;
367 };
368};
369 22
370export type CreateExecutorOptions = ExecutorRuntimeOptions & {23export type Executor = Omit<Promiseify<ExecutorEffect>, "runtime">;
371 backend: ExecutorBackend;24export type CreateExecutorOptions = CreateExecutorEffectOptions;
25export type {
26 ExecutorMcpSourceInput,
27 ExecutorSourceBatchInput,
28 ExecutorSourceInput,
29 ExecutorSourceOAuthInput,
372};30};
373 31
374const fromRuntime = (runtime: ExecutorRuntime): Executor => {32const toPromiseExecutor = (executor: ExecutorEffect): Executor => {
375 const installation = runtime.localInstallation;33 const run = <A, E>(effect: Effect.Effect<A, E, never>) =>
376 const scopeId = installation.scopeId;34 Effect.runPromise(effect);
377 const actorScopeId = installation.actorScopeId;
378 const provide = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
379 provideExecutorRuntime(effect, runtime);
380 const run = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
381 Effect.runPromise(provide(effect) as Effect.Effect<A, E, never>);
382 const provideSourceAuth = <A, E>(
383 execute: (
384 service: Effect.Effect.Success<typeof RuntimeSourceAuthServiceTag>,
385 ) => Effect.Effect<A, E, any>,
386 ) => provide(Effect.flatMap(RuntimeSourceAuthServiceTag, execute));
387 const createSdkSourceSession = () => {
388 const id = crypto.randomUUID();
389 return {
390 executionId: ExecutionIdSchema.make(`exec_sdk_${id}`),
391 interactionId: `executor.sdk.${id}` as never,
392 };
393 };
394 const effect = {
395 local: {
396 installation: () => provide(getLocalInstallation()),
397 config: () => provide(getLocalInstanceConfig()),
398 credentials: {
399 get: ({ sourceId, interactionId }) =>
400 provide(
401 getSourceCredentialInteraction({
402 scopeId,
403 sourceId,
404 interactionId,
405 }),
406 ),
407 submit: ({ sourceId, interactionId, action, token }) =>
408 provide(
409 submitSourceCredentialInteraction({
410 scopeId,
411 sourceId,
412 interactionId,
413 action,
414 token,
415 }),
416 ),
417 complete: ({ sourceId, state, code, error, errorDescription }) =>
418 provide(
419 completeSourceCredentialSetup({
420 scopeId,
421 sourceId,
422 state,
423 code,
424 error,
425 errorDescription,
426 }),
427 ),
428 },
429 },
430 secrets: {
431 list: () => provide(listLocalSecrets()),
432 create: (payload: CreateSecretPayload) => provide(createLocalSecret(payload)),
433 update: (input: { secretId: string; payload: UpdateSecretPayload }) =>
434 provide(updateLocalSecret(input)),
435 remove: (secretId: string) => provide(deleteLocalSecret(secretId)),
436 },
437 policies: {
438 list: () => provide(listPolicies(scopeId)),
439 create: (payload: CreatePolicyPayload) =>
440 provide(createPolicy({ scopeId, payload })),
441 get: (policyId: string) =>
442 provide(getPolicy({ scopeId, policyId: policyId as never })),
443 update: (policyId: string, payload: UpdatePolicyPayload) =>
444 provide(updatePolicy({ scopeId, policyId: policyId as never, payload })),
445 remove: (policyId: string) =>
446 provide(removePolicy({ scopeId, policyId: policyId as never })),
447 },
448 sources: {
449 add: (input: ExecutorSourceInput, options?: { baseUrl?: string | null }) =>
450 provideSourceAuth((service) => {
451 const session = createSdkSourceSession();
452 return service.addExecutorSource(
453 {
454 ...input,
455 scopeId,
456 actorScopeId: actorScopeId,
457 executionId: session.executionId,
458 interactionId: session.interactionId,
459 },
460 options,
461 );
462 }),
463 connect: (payload: ExecutorMcpSourceInput) =>
464 provideSourceAuth((service) =>
465 service.connectMcpSource({
466 ...payload,
467 scopeId,
468 actorScopeId: actorScopeId,
469 }),
470 ),
471 connectBatch: (payload: ExecutorSourceBatchInput) =>
472 provideSourceAuth((service) => {
473 const session = createSdkSourceSession();
474 return service.connectGoogleDiscoveryBatch({
475 ...payload,
476 scopeId,
477 actorScopeId: actorScopeId,
478 executionId: session.executionId,
479 interactionId: session.interactionId,
480 });
481 }),
482 discover: (input: {
483 url: string;
484 probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
485 }) => provide(discoverSource(input)),
486 list: () => provide(listSources({ scopeId, actorScopeId })),
487 create: (payload: CreateSourcePayload) =>
488 provide(createSource({ scopeId, actorScopeId, payload })),
489 get: (sourceId: Source["id"]) =>
490 provide(getSource({ scopeId, sourceId, actorScopeId })),
491 update: (sourceId: Source["id"], payload: UpdateSourcePayload) =>
492 provide(updateSource({ scopeId, sourceId, actorScopeId, payload })),
493 remove: (sourceId: Source["id"]) =>
494 provide(removeSource({ scopeId, sourceId })),
495 inspection: {
496 get: (sourceId: Source["id"]) =>
497 provide(getSourceInspection({ scopeId, sourceId })),
498 tool: ({ sourceId, toolPath }: { sourceId: Source["id"]; toolPath: string }) =>
499 provide(
500 getSourceInspectionToolDetail({
501 scopeId,
502 sourceId,
503 toolPath,
504 }),
505 ),
506 discover: ({
507 sourceId,
508 payload,
509 }: {
510 sourceId: Source["id"];
511 payload: Parameters<typeof discoverSourceInspectionTools>[0]["payload"];
512 }) =>
513 provide(
514 discoverSourceInspectionTools({
515 scopeId,
516 sourceId,
517 payload,
518 }),
519 ),
520 },
521 oauthClients: {
522 list: (providerKey: string) =>
523 provideSourceAuth((service) =>
524 service.listScopeOauthClients({
525 scopeId,
526 providerKey,
527 }),
528 ),
529 create: (payload: CreateScopeOauthClientPayload) =>
530 provideSourceAuth((service) =>
531 service.createScopeOauthClient({
532 scopeId,
533 providerKey: payload.providerKey,
534 label: payload.label,
535 oauthClient: payload.oauthClient,
536 }),
537 ),
538 remove: (oauthClientId: ScopeOauthClient["id"]) =>
539 provideSourceAuth((service) =>
540 service.removeScopeOauthClient({
541 scopeId,
542 oauthClientId,
543 }),
544 ),
545 },
546 providerGrants: {
547 remove: (grantId: ProviderAuthGrant["id"]) =>
548 provideSourceAuth((service) =>
549 service.removeProviderAuthGrant({
550 scopeId,
551 grantId,
552 }),
553 ),
554 },
555 },
556 oauth: {
557 startSourceAuth: (input: ExecutorSourceOAuthInput) =>
558 provideSourceAuth((service) =>
559 service.startSourceOAuthSession({
560 ...input,
561 scopeId,
562 actorScopeId: actorScopeId,
563 }),
564 ),
565 completeSourceAuth: ({ state, code, error, errorDescription }) =>
566 provideSourceAuth((service) =>
567 service.completeSourceOAuthSession({
568 state,
569 code,
570 error,
571 errorDescription,
572 }),
573 ),
574 completeProviderCallback: (input) =>
575 provideSourceAuth((service) =>
576 service.completeProviderOauthCallback({
577 ...input,
578 scopeId: input.scopeId ?? scopeId,
579 actorScopeId: input.actorScopeId ?? actorScopeId,
580 }),
581 ),
582 },
583 executions: {
584 create: (payload: CreateExecutionPayload) =>
585 provide(
586 createExecution({
587 scopeId,
588 payload,
589 createdByScopeId: actorScopeId,
590 }),
591 ),
592 get: (executionId: Execution["id"]) =>
593 provide(getExecution({ scopeId, executionId })),
594 resume: (executionId: Execution["id"], payload: ResumeExecutionPayload) =>
595 provide(
596 resumeExecution({
597 scopeId,
598 executionId,
599 payload,
600 resumedByScopeId: actorScopeId,
601 }),
602 ),
603 },
604 } satisfies Executor["effect"];
605 35
606 return {36 return {
607 runtime,37 installation: executor.installation,
608 installation,38 scopeId: executor.scopeId,
609 scopeId,39 actorScopeId: executor.actorScopeId,
610 actorScopeId,40 resolutionScopeIds: executor.resolutionScopeIds,
611 resolutionScopeIds: installation.resolutionScopeIds,41 close: () => executor.close(),
612 provide,
613 run,
614 close: () => runtime.close(),
615 effect,
616 local: {42 local: {
617 installation: () => run(effect.local.installation()),43 installation: () => run(executor.local.installation()),
618 config: () => run(effect.local.config()),44 config: () => run(executor.local.config()),
619 credentials: {45 credentials: {
620 get: ({ sourceId, interactionId }) =>46 get: (input) => run(executor.local.credentials.get(input)),
621 run(effect.local.credentials.get({ sourceId, interactionId })),47 submit: (input) => run(executor.local.credentials.submit(input)),
622 submit: ({ sourceId, interactionId, action, token }) =>48 complete: (input) => run(executor.local.credentials.complete(input)),
623 run(effect.local.credentials.submit({ sourceId, interactionId, action, token })),
624 complete: ({ sourceId, state, code, error, errorDescription }) =>
625 run(effect.local.credentials.complete({ sourceId, state, code, error, errorDescription })),
626 },49 },
627 },50 },
628 secrets: {51 secrets: {
629 list: () => run(effect.secrets.list()),52 list: () => run(executor.secrets.list()),
630 create: (payload) => run(effect.secrets.create(payload)),53 create: (payload) => run(executor.secrets.create(payload)),
631 update: (input) => run(effect.secrets.update(input)),54 update: (input) => run(executor.secrets.update(input)),
632 remove: (secretId) => run(effect.secrets.remove(secretId)),55 remove: (secretId) => run(executor.secrets.remove(secretId)),
633 },56 },
634 policies: {57 policies: {
635 list: () => run(effect.policies.list()),58 list: () => run(executor.policies.list()),
636 create: (payload) => run(effect.policies.create(payload)),59 create: (payload) => run(executor.policies.create(payload)),
637 get: (policyId) => run(effect.policies.get(policyId)),60 get: (policyId) => run(executor.policies.get(policyId)),
638 update: (policyId, payload) =>61 update: (policyId, payload) =>
639 run(effect.policies.update(policyId, payload)),62 run(executor.policies.update(policyId, payload)),
640 remove: async (policyId) =>63 remove: (policyId) => run(executor.policies.remove(policyId)),
641 (await run(effect.policies.remove(policyId))).removed,
642 },64 },
643 sources: {65 sources: {
644 add: (input, options) => run(effect.sources.add(input, options)),66 add: (input, options) => run(executor.sources.add(input, options)),
645 connect: (payload) => run(effect.sources.connect(payload)),67 connect: (payload) => run(executor.sources.connect(payload)),
646 connectBatch: (payload) => run(effect.sources.connectBatch(payload)),68 connectBatch: (payload) => run(executor.sources.connectBatch(payload)),
647 discover: (input) => run(effect.sources.discover(input)),69 discover: (input) => run(executor.sources.discover(input)),
648 list: () => run(effect.sources.list()),70 list: () => run(executor.sources.list()),
649 create: (payload) => run(effect.sources.create(payload)),71 create: (payload) => run(executor.sources.create(payload)),
650 get: (sourceId) => run(effect.sources.get(sourceId)),72 get: (sourceId) => run(executor.sources.get(sourceId)),
651 update: (sourceId, payload) =>73 update: (sourceId, payload) =>
652 run(effect.sources.update(sourceId, payload)),74 run(executor.sources.update(sourceId, payload)),
653 remove: async (sourceId) =>75 remove: (sourceId) => run(executor.sources.remove(sourceId)),
654 (await run(effect.sources.remove(sourceId))).removed,
655 inspection: {76 inspection: {
656 get: (sourceId) => run(effect.sources.inspection.get(sourceId)),77 get: (sourceId) => run(executor.sources.inspection.get(sourceId)),
657 tool: ({ sourceId, toolPath }) =>78 tool: (input) => run(executor.sources.inspection.tool(input)),
658 run(effect.sources.inspection.tool({ sourceId, toolPath })),79 discover: (input) => run(executor.sources.inspection.discover(input)),
659 discover: ({ sourceId, payload }) =>
660 run(effect.sources.inspection.discover({ sourceId, payload })),
661 },80 },
662 oauthClients: {81 oauthClients: {
663 list: (providerKey) =>82 list: (providerKey) =>
664 run(effect.sources.oauthClients.list(providerKey)),83 run(executor.sources.oauthClients.list(providerKey)),
665 create: (payload) => run(effect.sources.oauthClients.create(payload)),84 create: (payload) => run(executor.sources.oauthClients.create(payload)),
666 remove: (oauthClientId) =>85 remove: (oauthClientId) =>
667 run(effect.sources.oauthClients.remove(oauthClientId)),86 run(executor.sources.oauthClients.remove(oauthClientId)),
668 },87 },
669 providerGrants: {88 providerGrants: {
670 remove: (grantId) =>89 remove: (grantId) =>
671 run(effect.sources.providerGrants.remove(grantId)),90 run(executor.sources.providerGrants.remove(grantId)),
672 },91 },
673 },92 },
674 oauth: {93 oauth: {
675 startSourceAuth: (input) => run(effect.oauth.startSourceAuth(input)),94 startSourceAuth: (input) => run(executor.oauth.startSourceAuth(input)),
676 completeSourceAuth: ({ state, code, error, errorDescription }) =>95 completeSourceAuth: (input) =>
677 run(effect.oauth.completeSourceAuth({ state, code, error, errorDescription })),96 run(executor.oauth.completeSourceAuth(input)),
678 completeProviderCallback: (input) =>97 completeProviderCallback: (input) =>
679 run(effect.oauth.completeProviderCallback(input)),98 run(executor.oauth.completeProviderCallback(input)),
680 },99 },
681 executions: {100 executions: {
682 create: (payload) => run(effect.executions.create(payload)),101 create: (payload) => run(executor.executions.create(payload)),
683 get: (executionId) => run(effect.executions.get(executionId)),102 get: (executionId) => run(executor.executions.get(executionId)),
684 resume: (executionId, payload) =>103 resume: (executionId, payload) =>
685 run(effect.executions.resume(executionId, payload)),104 run(executor.executions.resume(executionId, payload)),
686 },105 },
687 };106 };
688};107};
689 108
690export const createExecutorEffect = (
691 options: CreateExecutorOptions,
692): Effect.Effect<Executor, Error> =>
693 Effect.map(
694 options.backend.createRuntime({
695 executionResolver: options.executionResolver,
696 createInternalToolMap: options.createInternalToolMap,
697 resolveSecretMaterial: options.resolveSecretMaterial,
698 getLocalServerBaseUrl: options.getLocalServerBaseUrl,
699 }),
700 fromRuntime,
701 );
702
703export const createExecutor = async (109export const createExecutor = async (
704 options: CreateExecutorOptions,110 options: CreateExecutorOptions,
705): Promise<Executor> => Effect.runPromise(createExecutorEffect(options));111): Promise<Executor> => toPromiseExecutor(await Effect.runPromise(createExecutorEffect(options)));
packages/platform/sdk/src/runtime/execution/http.test.ts+3−3
@@ -27,7 +27,7 @@ import { · 26 unmodified lines
27 27
28import {28import {
29 createLocalExecutorRuntime as createExecutorRuntime,29 createLocalExecutorRuntime as createExecutorRuntime,
30} from "../../../../sdk-file/src/index";30} from "../../../../sdk-file/src/runtime";
31import {31import {
32 withExecutorApiClient,32 withExecutorApiClient,
33} from "./test-http-client";33} from "./test-http-client";
@@ -100,7 +100,7 @@ describe("execution-http", () => { · 66 unmodified lines
100 (client) =>100 (client) =>
101 client.executions.create({101 client.executions.create({
102 path: {102 path: {
103 scopeId: installation.scopeId,103 workspaceId: installation.scopeId,
104 },104 },
105 payload: {105 payload: {
106 code: "return await tools.math.add({ a: 20, b: 22 });",106 code: "return await tools.math.add({ a: 20, b: 22 });",
@@ -120,7 +120,7 @@ describe("execution-http", () => { · 13 unmodified lines
120 (client) =>120 (client) =>
121 client.executions.get({121 client.executions.get({
122 path: {122 path: {
123 scopeId: installation.scopeId,123 workspaceId: installation.scopeId,
124 executionId: createExecution.execution.id,124 executionId: createExecution.execution.id,
125 },125 },
126 }),126 }),
packages/platform/sdk/src/runtime/execution/mcp-resume.test.ts+3−3
@@ -58,7 +58,7 @@ import { · 57 unmodified lines
58} from "../index";58} from "../index";
59import {59import {
60 createLocalExecutorRuntime as createExecutorRuntime,60 createLocalExecutorRuntime as createExecutorRuntime,
61} from "../../../../sdk-file/src/index";61} from "../../../../sdk-file/src/runtime";
62import {62import {
63 withExecutorApiClient,63 withExecutorApiClient,
64} from "./test-http-client";64} from "./test-http-client";
@@ -353,7 +353,7 @@ describe("execution-mcp-resume", () => { · 288 unmodified lines
353 (client) =>353 (client) =>
354 client.executions.create({354 client.executions.create({
355 path: {355 path: {
356 scopeId: installation.scopeId,356 workspaceId: installation.scopeId,
357 },357 },
358 payload: {358 payload: {
359 code: 'return await tools.source.form.gated_echo({ value: "from-control-plane" });',359 code: 'return await tools.source.form.gated_echo({ value: "from-control-plane" });',
@@ -387,7 +387,7 @@ describe("execution-mcp-resume", () => { · 27 unmodified lines
387 (client) =>387 (client) =>
388 client.executions.resume({388 client.executions.resume({
389 path: {389 path: {
390 scopeId: installation.scopeId,390 workspaceId: installation.scopeId,
391 executionId: created.execution.id,391 executionId: created.execution.id,
392 },392 },
393 payload: {393 payload: {
packages/platform/sdk/src/runtime/execution/runtime-config.test.ts+4−4
@@ -19,7 +19,7 @@ import * as Effect from "effect/Effect"; · 18 unmodified lines
19 19
20import {20import {
21 createLocalExecutorRuntime as createExecutorRuntime,21 createLocalExecutorRuntime as createExecutorRuntime,
22} from "../../../../sdk-file/src/index";22} from "../../../../sdk-file/src/runtime";
23import {23import {
24 withExecutorApiClient,24 withExecutorApiClient,
25} from "./test-http-client";25} from "./test-http-client";
@@ -74,7 +74,7 @@ describe("execution runtime config", () => { · 48 unmodified lines
74 (client) =>74 (client) =>
75 client.executions.create({75 client.executions.create({
76 path: {76 path: {
77 scopeId: installation.scopeId,77 workspaceId: installation.scopeId,
78 },78 },
79 payload: {79 payload: {
80 code: 'await fetch("https://example.com"); return 1;',80 code: 'await fetch("https://example.com"); return 1;',
@@ -106,7 +106,7 @@ describe("execution runtime config", () => { · 25 unmodified lines
106 (client) =>106 (client) =>
107 client.executions.create({107 client.executions.create({
108 path: {108 path: {
109 scopeId: installation.scopeId,109 workspaceId: installation.scopeId,
110 },110 },
111 payload: {111 payload: {
112 code: 'await fetch("https://example.com"); return 1;',112 code: 'await fetch("https://example.com"); return 1;',
@@ -138,7 +138,7 @@ describe("execution runtime config", () => { · 25 unmodified lines
138 (client) =>138 (client) =>
139 client.executions.create({139 client.executions.create({
140 path: {140 path: {
141 scopeId: installation.scopeId,141 workspaceId: installation.scopeId,
142 },142 },
143 payload: {143 payload: {
144 code: 'return typeof Deno !== "undefined";',144 code: 'return typeof Deno !== "undefined";',
packages/platform/sdk/src/runtime/execution/test-http-client.ts+5−1
@@ -8,13 +8,17 @@ import { · 7 unmodified lines
8} from "@effect/platform-node";8} from "@effect/platform-node";
9import {9import {
10 ExecutorApi,10 ExecutorApi,
11 createExecutorApiLayer,
12} from "../../../../api/src/index";11} from "../../../../api/src/index";
12import {
13 createExecutorApiLayer,
14} from "../../../../api/src/http";
13import * as Effect from "effect/Effect";15import * as Effect from "effect/Effect";
14import * as Layer from "effect/Layer";16import * as Layer from "effect/Layer";
15 17
16import {18import {
17 createExecutorEffect,19 createExecutorEffect,
20} from "../../effect";
21import {
18 type ExecutorRuntime,22 type ExecutorRuntime,
19} from "../../index";23} from "../../index";
20 24
packages/platform/sdk/src/runtime/executor-runtime.test.ts+4−2
@@ -81,7 +81,6 @@ import { · 80 unmodified lines
81} from "./effect-errors";81} from "./effect-errors";
82import {82import {
83 buildLocalSourceArtifact,83 buildLocalSourceArtifact,
84 createLocalExecutorRuntime as createExecutorRuntime,
85 deriveLocalInstallation,84 deriveLocalInstallation,
86 readLocalSourceArtifact,85 readLocalSourceArtifact,
87 resolveLocalWorkspaceContext,86 resolveLocalWorkspaceContext,
@@ -89,7 +88,10 @@ import { · 1 unmodified line
89 writeLocalSourceArtifact,88 writeLocalSourceArtifact,
90 writeProjectLocalExecutorConfig,89 writeProjectLocalExecutorConfig,
91 writeLocalWorkspaceState,90 writeLocalWorkspaceState,
92} from "../../../sdk-file/src/index";91} from "../../../sdk-file/src/effect";
92import {
93 createLocalExecutorRuntime as createExecutorRuntime,
94} from "../../../sdk-file/src/runtime";
93import {95import {
94 decodeSourceCredentialInteraction,96 decodeSourceCredentialInteraction,
95 submitSourceCredentialInteraction,97 submitSourceCredentialInteraction,
packages/platform/server/src/index.ts+3−3
@@ -10,13 +10,13 @@ import { FileSystem, HttpApiBuilder, HttpServer } from "@effect/platform"; · 9 unmodified lines
10import { NodeFileSystem } from "@effect/platform-node";10import { NodeFileSystem } from "@effect/platform-node";
11import {11import {
12 createExecutorApiLayer,12 createExecutorApiLayer,
13} from "@executor/platform-api";13} from "@executor/platform-api/http";
14import { createExecutorMcpRequestHandler } from "@executor/executor-mcp";14import { createExecutorMcpRequestHandler } from "@executor/executor-mcp";
15import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";15import { createWorkspaceExecutorAdminToolMap } from "@executor/platform-internal";
16import {16import {
17 createExecutorEffect,17 createExecutorEffect,
18 type Executor,18 type ExecutorEffect as Executor,
19} from "@executor/platform-sdk";19} from "@executor/platform-sdk/effect";
20import { createLocalExecutorBackend } from "@executor/platform-sdk-file";20import { createLocalExecutorBackend } from "@executor/platform-sdk-file";
21import {21import {
22 type ResolveExecutionEnvironment,22 type ResolveExecutionEnvironment,