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