Unified · Split
| @@ -14,10 +14,10 @@ import { · 13 unmodified lines | ||
| 14 | 14 | import { |
| 15 | 15 | createExecutorBackend, |
| 16 | 16 | type ExecutorBackend, |
| 17 | − type ExecutorBackendServices, | |
| 18 | − type ExecutorScopeConfigBackend, | |
| 19 | − type ExecutorScopeStateBackend, | |
| 20 | − type ExecutorSourceArtifactBackend, | |
| 17 | + type ExecutorBackendRepositories, | |
| 18 | + type ExecutorWorkspaceConfigRepository, | |
| 19 | + type ExecutorWorkspaceStateRepository, | |
| 20 | + type ExecutorWorkspaceSourceArtifactRepository, | |
| 21 | 21 | } from "@executor/platform-sdk"; |
| 22 | 22 | import type { |
| 23 | 23 | AuthArtifact, |
| @@ -53,10 +53,13 @@ export type CreateSqliteExecutorBackendOptions = { · 29 unmodified lines | ||
| 53 | 53 | actorScopeId?: string; |
| 54 | 54 | }; |
| 55 | 55 | |
| 56 | −type ScopeConfig = Awaited<ReturnType<ExecutorScopeConfigBackend["load"]>>; | |
| 57 | −type ScopeState = Awaited<ReturnType<ExecutorScopeStateBackend["load"]>>; | |
| 58 | −type SourceArtifact = ReturnType<ExecutorSourceArtifactBackend["build"]>; | |
| 59 | −type SourceArtifactBuildInput = Parameters<ExecutorSourceArtifactBackend["build"]>[0]; | |
| 56 | +type ScopeConfig = Awaited<ReturnType<ExecutorWorkspaceConfigRepository["load"]>>; | |
| 57 | +type ScopeState = Awaited<ReturnType<ExecutorWorkspaceStateRepository["load"]>>; | |
| 58 | +type SourceArtifact = | |
| 59 | + ReturnType<ExecutorWorkspaceSourceArtifactRepository["build"]>; | |
| 60 | +type SourceArtifactBuildInput = Parameters< | |
| 61 | + ExecutorWorkspaceSourceArtifactRepository["build"] | |
| 62 | +>[0]; | |
| 60 | 63 | |
| 61 | 64 | type SecretMaterialSummary = { |
| 62 | 65 | id: string; |
| @@ -924,7 +927,7 @@ export const createSqliteExecutorBackend = ( · 861 unmodified lines | ||
| 924 | 927 | : (options.databasePath ?? ":memory:"); |
| 925 | 928 | |
| 926 | 929 | return createExecutorBackend({ |
| 927 | − loadServices: () => { | |
| 930 | + loadRepositories: () => { | |
| 928 | 931 | const store = openSqliteStore(databasePath); |
| 929 | 932 | const { auth, secrets, executions } = createStorageDomains(store); |
| 930 | 933 | |
| @@ -937,9 +940,21 @@ export const createSqliteExecutorBackend = ( · 6 unmodified lines | ||
| 937 | 940 | databasePath, |
| 938 | 941 | }, |
| 939 | 942 | }, |
| 940 | − storage: { | |
| 941 | − installation: { | |
| 942 | − load: () => { | |
| 943 | + installation: { | |
| 944 | + load: () => { | |
| 945 | + const row = store.db.select().from(installations).where(eq(installations.key, "active")).get(); | |
| 946 | + return row | |
| 947 | + ? { | |
| 948 | + scopeId: row.scopeId as LocalInstallation["scopeId"], | |
| 949 | + actorScopeId: row.actorScopeId as LocalInstallation["actorScopeId"], | |
| 950 | + resolutionScopeIds: parseJson<LocalInstallation["resolutionScopeIds"]>( | |
| 951 | + row.resolutionScopeIdsJson, | |
| 952 | + ), | |
| 953 | + } | |
| 954 | + : createInstallation(options); | |
| 955 | + }, | |
| 956 | + getOrProvision: () => { | |
| 957 | + const installation = (() => { | |
| 943 | 958 | const row = store.db.select().from(installations).where(eq(installations.key, "active")).get(); |
| 944 | 959 | return row |
| 945 | 960 | ? { |
| @@ -950,37 +965,25 @@ export const createSqliteExecutorBackend = ( · 4 unmodified lines | ||
| 950 | 965 | ), |
| 951 | 966 | } |
| 952 | 967 | : createInstallation(options); |
| 953 | − }, | |
| 954 | − getOrProvision: () => { | |
| 955 | − const installation = (() => { | |
| 956 | − const row = store.db.select().from(installations).where(eq(installations.key, "active")).get(); | |
| 957 | − return row | |
| 958 | − ? { | |
| 959 | − scopeId: row.scopeId as LocalInstallation["scopeId"], | |
| 960 | − actorScopeId: row.actorScopeId as LocalInstallation["actorScopeId"], | |
| 961 | − resolutionScopeIds: parseJson<LocalInstallation["resolutionScopeIds"]>( | |
| 962 | − row.resolutionScopeIdsJson, | |
| 963 | − ), | |
| 964 | − } | |
| 965 | − : createInstallation(options); | |
| 966 | − })(); | |
| 967 | − store.db.insert(installations).values({ | |
| 968 | − key: "active", | |
| 968 | + })(); | |
| 969 | + store.db.insert(installations).values({ | |
| 970 | + key: "active", | |
| 971 | + scopeId: installation.scopeId, | |
| 972 | + actorScopeId: installation.actorScopeId, | |
| 973 | + resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds), | |
| 974 | + }).onConflictDoUpdate({ | |
| 975 | + target: installations.key, | |
| 976 | + set: { | |
| 969 | 977 | scopeId: installation.scopeId, |
| 970 | 978 | actorScopeId: installation.actorScopeId, |
| 971 | 979 | resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds), |
| 972 | − }).onConflictDoUpdate({ | |
| 973 | − target: installations.key, | |
| 974 | − set: { | |
| 975 | − scopeId: installation.scopeId, | |
| 976 | − actorScopeId: installation.actorScopeId, | |
| 977 | − resolutionScopeIdsJson: JSON.stringify(installation.resolutionScopeIds), | |
| 978 | − }, | |
| 979 | − }).run(); | |
| 980 | − return installation; | |
| 981 | − }, | |
| 980 | + }, | |
| 981 | + }).run(); | |
| 982 | + return installation; | |
| 982 | 983 | }, |
| 983 | − scopeConfig: { | |
| 984 | + }, | |
| 985 | + workspace: { | |
| 986 | + config: { | |
| 984 | 987 | load: () => { |
| 985 | 988 | const row = store.db.select().from(scopeConfigs).where(eq(scopeConfigs.key, "project")).get(); |
| 986 | 989 | const projectConfig = row |
| @@ -1003,7 +1006,7 @@ export const createSqliteExecutorBackend = ( · 16 unmodified lines | ||
| 1003 | 1006 | }, |
| 1004 | 1007 | resolveRelativePath: ({ path, scopeRoot }) => resolvePath(scopeRoot, path), |
| 1005 | 1008 | }, |
| 1006 | − scopeState: { | |
| 1009 | + state: { | |
| 1007 | 1010 | load: () => { |
| 1008 | 1011 | const row = store.db.select().from(scopeStates).where(eq(scopeStates.key, "active")).get(); |
| 1009 | 1012 | return row ? parseJson<ScopeState>(row.stateJson) : defaultScopeState(); |
| @@ -1039,55 +1042,52 @@ export const createSqliteExecutorBackend = ( · 29 unmodified lines | ||
| 1039 | 1042 | store.db.delete(sourceArtifacts).where(eq(sourceArtifacts.sourceId, sourceId)).run(); |
| 1040 | 1043 | }, |
| 1041 | 1044 | }, |
| 1042 | − auth, | |
| 1043 | − secrets: { | |
| 1044 | − ...secrets, | |
| 1045 | − resolve: ({ ref }) => { | |
| 1046 | − const material = secrets.getById( | |
| 1047 | − ref.handle as SecretMaterial["id"], | |
| 1048 | − ); | |
| 1049 | − if (!material || material.value === null) { | |
| 1050 | − throw new Error(`Missing secret material ${ref.handle}`); | |
| 1051 | − } | |
| 1052 | − return material.value; | |
| 1053 | − }, | |
| 1054 | − store: ({ purpose, value, name, providerId }) => { | |
| 1055 | − const now = Date.now(); | |
| 1056 | − const id = SecretMaterialIdSchema.make(`secret_${randomUUID()}`); | |
| 1057 | − const material: SecretMaterial = { | |
| 1058 | − id, | |
| 1059 | − providerId: providerId ?? SQLITE_SECRET_PROVIDER_ID, | |
| 1060 | − handle: id, | |
| 1061 | − name: name ?? null, | |
| 1062 | − purpose, | |
| 1063 | − value, | |
| 1064 | − createdAt: now, | |
| 1065 | − updatedAt: now, | |
| 1066 | − }; | |
| 1067 | − secrets.upsert(material); | |
| 1068 | − return { | |
| 1069 | − providerId: material.providerId, | |
| 1070 | − handle: material.handle, | |
| 1071 | − } satisfies SecretRef; | |
| 1072 | − }, | |
| 1073 | − delete: (ref) => | |
| 1074 | − secrets.removeById(ref.handle as SecretMaterial["id"]), | |
| 1075 | − update: ({ ref, name, value }) => { | |
| 1076 | − const updated = secrets.updateById( | |
| 1077 | − ref.handle as SecretMaterial["id"], | |
| 1078 | − { name, value }, | |
| 1079 | − ); | |
| 1080 | − if (!updated) { | |
| 1081 | − throw new Error(`Missing secret material ${ref.handle}`); | |
| 1082 | − } | |
| 1083 | − return updated; | |
| 1084 | − }, | |
| 1045 | + sourceAuth: auth, | |
| 1046 | + }, | |
| 1047 | + secrets: { | |
| 1048 | + ...secrets, | |
| 1049 | + resolve: ({ ref }) => { | |
| 1050 | + const material = secrets.getById( | |
| 1051 | + ref.handle as SecretMaterial["id"], | |
| 1052 | + ); | |
| 1053 | + if (!material || material.value === null) { | |
| 1054 | + throw new Error(`Missing secret material ${ref.handle}`); | |
| 1055 | + } | |
| 1056 | + return material.value; | |
| 1085 | 1057 | }, |
| 1086 | − executions, | |
| 1087 | − close: async () => { | |
| 1088 | − store.close(); | |
| 1058 | + store: ({ purpose, value, name, providerId }) => { | |
| 1059 | + const now = Date.now(); | |
| 1060 | + const id = SecretMaterialIdSchema.make(`secret_${randomUUID()}`); | |
| 1061 | + const material: SecretMaterial = { | |
| 1062 | + id, | |
| 1063 | + providerId: providerId ?? SQLITE_SECRET_PROVIDER_ID, | |
| 1064 | + handle: id, | |
| 1065 | + name: name ?? null, | |
| 1066 | + purpose, | |
| 1067 | + value, | |
| 1068 | + createdAt: now, | |
| 1069 | + updatedAt: now, | |
| 1070 | + }; | |
| 1071 | + secrets.upsert(material); | |
| 1072 | + return { | |
| 1073 | + providerId: material.providerId, | |
| 1074 | + handle: material.handle, | |
| 1075 | + } satisfies SecretRef; | |
| 1076 | + }, | |
| 1077 | + delete: (ref) => | |
| 1078 | + secrets.removeById(ref.handle as SecretMaterial["id"]), | |
| 1079 | + update: ({ ref, name, value }) => { | |
| 1080 | + const updated = secrets.updateById( | |
| 1081 | + ref.handle as SecretMaterial["id"], | |
| 1082 | + { name, value }, | |
| 1083 | + ); | |
| 1084 | + if (!updated) { | |
| 1085 | + throw new Error(`Missing secret material ${ref.handle}`); | |
| 1086 | + } | |
| 1087 | + return updated; | |
| 1089 | 1088 | }, |
| 1090 | 1089 | }, |
| 1090 | + executions, | |
| 1091 | 1091 | instanceConfig: { |
| 1092 | 1092 | resolve: () => ({ |
| 1093 | 1093 | platform: "sqlite-sdk-example", |
| @@ -1101,7 +1101,10 @@ export const createSqliteExecutorBackend = ( · 7 unmodified lines | ||
| 1101 | 1101 | defaultSecretStoreProvider: SQLITE_SECRET_PROVIDER_ID, |
| 1102 | 1102 | }), |
| 1103 | 1103 | }, |
| 1104 | − } satisfies ExecutorBackendServices; | |
| 1104 | + close: async () => { | |
| 1105 | + store.close(); | |
| 1106 | + }, | |
| 1107 | + } satisfies ExecutorBackendRepositories; | |
| 1105 | 1108 | }, |
| 1106 | 1109 | }); |
| 1107 | 1110 | }; |
| @@ -2,7 +2,7 @@ export type { CreateLocalExecutorBackendOptions } from "./index"; · 1 unmodified line | ||
| 2 | 2 | export { |
| 3 | 3 | buildLocalSourceArtifact, |
| 4 | 4 | createLocalExecutorEffect, |
| 5 | − createLocalExecutorServicesEffect, | |
| 5 | + createLocalExecutorRepositoriesEffect, | |
| 6 | 6 | deriveLocalInstallation, |
| 7 | 7 | getOrProvisionLocalInstallation, |
| 8 | 8 | loadLocalExecutorConfig, |
| @@ -5,10 +5,10 @@ import { · 4 unmodified lines | ||
| 5 | 5 | createExecutorBackend, |
| 6 | 6 | type Executor, |
| 7 | 7 | type ExecutorBackend, |
| 8 | − type ExecutorBackendServices, | |
| 9 | − type ExecutorLocalToolBackend, | |
| 10 | − type ExecutorSourceTypeDeclarationsBackend, | |
| 8 | + type ExecutorBackendRepositories, | |
| 11 | 9 | type ExecutorScopeDescriptor, |
| 10 | + type ExecutorWorkspaceLocalToolRepository, | |
| 11 | + type ExecutorWorkspaceSourceTypeDeclarationsRepository, | |
| 12 | 12 | } from "@executor/platform-sdk"; |
| 13 | 13 | import { |
| 14 | 14 | createExecutorEffect, |
| @@ -176,14 +176,14 @@ const createBoundSourceArtifactStore = ( · 161 unmodified lines | ||
| 176 | 176 | const createBoundLocalToolRuntimeLoader = ( |
| 177 | 177 | context: ResolvedLocalWorkspaceContext, |
| 178 | 178 | fileSystem: FileSystem.FileSystem, |
| 179 | −): ExecutorLocalToolBackend => ({ | |
| 179 | +): ExecutorWorkspaceLocalToolRepository => ({ | |
| 180 | 180 | load: () => |
| 181 | 181 | bindFileSystem(fileSystem, loadLocalToolRuntime(context)), |
| 182 | 182 | }); |
| 183 | 183 | |
| 184 | 184 | const createBoundSourceTypeDeclarationsRefresher = ( |
| 185 | 185 | context: ResolvedLocalWorkspaceContext, |
| 186 | −): ExecutorSourceTypeDeclarationsBackend => ({ | |
| 186 | +): ExecutorWorkspaceSourceTypeDeclarationsRepository => ({ | |
| 187 | 187 | refreshWorkspaceInBackground: ({ entries }) => |
| 188 | 188 | Effect.sync(() => { |
| 189 | 189 | refreshWorkspaceSourceTypeDeclarationsInBackground({ |
| @@ -217,10 +217,10 @@ const toExecutorScopeContext = ( · 27 unmodified lines | ||
| 217 | 217 | }, |
| 218 | 218 | }); |
| 219 | 219 | |
| 220 | −export const createLocalExecutorServicesEffect = ( | |
| 220 | +export const createLocalExecutorRepositoriesEffect = ( | |
| 221 | 221 | options: CreateLocalExecutorBackendOptions = {}, |
| 222 | 222 | runtimeOptions: ExecutorRuntimeOptions = {}, |
| 223 | −): Effect.Effect<ExecutorBackendServices, Error> => | |
| 223 | +): Effect.Effect<ExecutorBackendRepositories, Error> => | |
| 224 | 224 | Effect.gen(function* () { |
| 225 | 225 | const fileSystem = yield* FileSystem.FileSystem; |
| 226 | 226 | const workspaceContext = yield* bindFileSystem( |
| @@ -252,10 +252,10 @@ export const createLocalExecutorServicesEffect = ( · 25 unmodified lines | ||
| 252 | 252 | |
| 253 | 253 | return { |
| 254 | 254 | scope: toExecutorScopeContext(workspaceContext), |
| 255 | − storage: { | |
| 256 | − installation: createBoundInstallationStore(workspaceContext), | |
| 257 | − scopeConfig: workspaceConfigStore, | |
| 258 | − scopeState: createBoundWorkspaceStateStore( | |
| 255 | + installation: createBoundInstallationStore(workspaceContext), | |
| 256 | + workspace: { | |
| 257 | + config: workspaceConfigStore, | |
| 258 | + state: createBoundWorkspaceStateStore( | |
| 259 | 259 | workspaceContext, |
| 260 | 260 | fileSystem, |
| 261 | 261 | ), |
| @@ -263,7 +263,7 @@ export const createLocalExecutorServicesEffect = ( · 1 unmodified line | ||
| 263 | 263 | workspaceContext, |
| 264 | 264 | fileSystem, |
| 265 | 265 | ), |
| 266 | − auth: { | |
| 266 | + sourceAuth: { | |
| 267 | 267 | artifacts: executorStateStorage.executorState.authArtifacts, |
| 268 | 268 | leases: executorStateStorage.executorState.authLeases, |
| 269 | 269 | sourceOauthClients: |
| @@ -275,45 +275,45 @@ export const createLocalExecutorServicesEffect = ( · 5 unmodified lines | ||
| 275 | 275 | sourceSessions: |
| 276 | 276 | executorStateStorage.executorState.sourceAuthSessions, |
| 277 | 277 | }, |
| 278 | − secrets: { | |
| 279 | − ...executorStateStorage.executorState.secretMaterials, | |
| 280 | − resolve: resolveSecretMaterial, | |
| 281 | − store: createDefaultSecretMaterialStorer({ | |
| 282 | − executorState: executorStateStorage.executorState, | |
| 283 | − }), | |
| 284 | − delete: createDefaultSecretMaterialDeleter({ | |
| 285 | − executorState: executorStateStorage.executorState, | |
| 286 | − }), | |
| 287 | − update: createDefaultSecretMaterialUpdater({ | |
| 288 | − executorState: executorStateStorage.executorState, | |
| 289 | − }), | |
| 290 | − }, | |
| 291 | − executions: { | |
| 292 | − runs: executorStateStorage.executorState.executions, | |
| 293 | − interactions: | |
| 294 | − executorStateStorage.executorState.executionInteractions, | |
| 295 | − steps: executorStateStorage.executorState.executionSteps, | |
| 296 | − }, | |
| 297 | − close: executorStateStorage.close, | |
| 278 | + localTools: createBoundLocalToolRuntimeLoader( | |
| 279 | + workspaceContext, | |
| 280 | + fileSystem, | |
| 281 | + ), | |
| 282 | + sourceTypeDeclarations: | |
| 283 | + createBoundSourceTypeDeclarationsRefresher(workspaceContext), | |
| 284 | + }, | |
| 285 | + secrets: { | |
| 286 | + ...executorStateStorage.executorState.secretMaterials, | |
| 287 | + resolve: resolveSecretMaterial, | |
| 288 | + store: createDefaultSecretMaterialStorer({ | |
| 289 | + executorState: executorStateStorage.executorState, | |
| 290 | + }), | |
| 291 | + delete: createDefaultSecretMaterialDeleter({ | |
| 292 | + executorState: executorStateStorage.executorState, | |
| 293 | + }), | |
| 294 | + update: createDefaultSecretMaterialUpdater({ | |
| 295 | + executorState: executorStateStorage.executorState, | |
| 296 | + }), | |
| 297 | + }, | |
| 298 | + executions: { | |
| 299 | + runs: executorStateStorage.executorState.executions, | |
| 300 | + interactions: | |
| 301 | + executorStateStorage.executorState.executionInteractions, | |
| 302 | + steps: executorStateStorage.executorState.executionSteps, | |
| 298 | 303 | }, |
| 299 | − localTools: createBoundLocalToolRuntimeLoader( | |
| 300 | − workspaceContext, | |
| 301 | − fileSystem, | |
| 302 | − ), | |
| 303 | − sourceTypeDeclarations: | |
| 304 | − createBoundSourceTypeDeclarationsRefresher(workspaceContext), | |
| 305 | 304 | instanceConfig: { |
| 306 | 305 | resolve: createLocalInstanceConfigResolver(), |
| 307 | 306 | }, |
| 308 | − } satisfies ExecutorBackendServices; | |
| 307 | + close: executorStateStorage.close, | |
| 308 | + } satisfies ExecutorBackendRepositories; | |
| 309 | 309 | }).pipe(Effect.provide(NodeFileSystem.layer)); |
| 310 | 310 | |
| 311 | 311 | export const createLocalExecutorBackend = ( |
| 312 | 312 | options: CreateLocalExecutorBackendOptions = {}, |
| 313 | 313 | ): ExecutorBackend => |
| 314 | 314 | createExecutorBackend({ |
| 315 | − loadServices: (runtimeOptions: ExecutorRuntimeOptions) => | |
| 316 | − createLocalExecutorServicesEffect(options, runtimeOptions), | |
| 315 | + loadRepositories: (runtimeOptions: ExecutorRuntimeOptions) => | |
| 316 | + createLocalExecutorRepositoriesEffect(options, runtimeOptions), | |
| 317 | 317 | }); |
| 318 | 318 | |
| 319 | 319 | export const createLocalExecutorRuntime = ( |
| @@ -47,15 +47,19 @@ type PublicizeObject<T> = { · 46 unmodified lines | ||
| 47 | 47 | : T[Key]; |
| 48 | 48 | }; |
| 49 | 49 | |
| 50 | −export type ExecutorInstallationBackend = PublicizeObject<BoundInstallationStore>; | |
| 51 | −export type ExecutorScopeConfigBackend = PublicizeObject<BoundScopeConfigStore>; | |
| 52 | −export type ExecutorScopeStateBackend = PublicizeObject<BoundScopeStateStore>; | |
| 53 | −export type ExecutorSourceArtifactBackend = PublicizeObject<BoundSourceArtifactStore>; | |
| 54 | −export type ExecutorLocalToolBackend = PublicizeObject<BoundLocalToolRuntimeLoader>; | |
| 55 | −export type ExecutorSourceTypeDeclarationsBackend = PublicizeObject< | |
| 50 | +export type ExecutorInstallationRepository = PublicizeObject<BoundInstallationStore>; | |
| 51 | +export type ExecutorWorkspaceConfigRepository = PublicizeObject<BoundScopeConfigStore>; | |
| 52 | +export type ExecutorWorkspaceStateRepository = PublicizeObject<BoundScopeStateStore>; | |
| 53 | +export type ExecutorWorkspaceSourceArtifactRepository = PublicizeObject< | |
| 54 | + BoundSourceArtifactStore | |
| 55 | +>; | |
| 56 | +export type ExecutorWorkspaceLocalToolRepository = PublicizeObject< | |
| 57 | + BoundLocalToolRuntimeLoader | |
| 58 | +>; | |
| 59 | +export type ExecutorWorkspaceSourceTypeDeclarationsRepository = PublicizeObject< | |
| 56 | 60 | BoundSourceTypeDeclarationsRefresher |
| 57 | 61 | >; |
| 58 | −export type ExecutorAuthBackend = { | |
| 62 | +export type ExecutorWorkspaceSourceAuthRepository = { | |
| 59 | 63 | artifacts: PublicizeObject<RuntimeAuthStorageServices["artifacts"]>; |
| 60 | 64 | leases: PublicizeObject<RuntimeAuthStorageServices["leases"]>; |
| 61 | 65 | sourceOauthClients: PublicizeObject<RuntimeAuthStorageServices["sourceOauthClients"]>; |
| @@ -63,31 +67,33 @@ export type ExecutorAuthBackend = { · 1 unmodified line | ||
| 63 | 67 | providerGrants: PublicizeObject<RuntimeAuthStorageServices["providerGrants"]>; |
| 64 | 68 | sourceSessions: PublicizeObject<RuntimeAuthStorageServices["sourceSessions"]>; |
| 65 | 69 | }; |
| 66 | −export type ExecutorSecretsBackend = PublicizeObject<RuntimeSecretsStorageServices>; | |
| 67 | −export type ExecutorExecutionsBackend = { | |
| 70 | +export type ExecutorSecretRepository = PublicizeObject<RuntimeSecretsStorageServices>; | |
| 71 | +export type ExecutorExecutionRepository = { | |
| 68 | 72 | runs: PublicizeObject<RuntimeExecutionStorageServices["runs"]>; |
| 69 | 73 | interactions: PublicizeObject<RuntimeExecutionStorageServices["interactions"]>; |
| 70 | 74 | steps: PublicizeObject<RuntimeExecutionStorageServices["steps"]>; |
| 71 | 75 | }; |
| 72 | −export type ExecutorInstanceConfigBackend = PublicizeObject<RuntimeInstanceConfigService>; | |
| 76 | +export type ExecutorInstanceConfigRepository = PublicizeObject< | |
| 77 | + RuntimeInstanceConfigService | |
| 78 | +>; | |
| 73 | 79 | |
| 74 | −export type ExecutorStorageBackend = { | |
| 75 | − installation: ExecutorInstallationBackend; | |
| 76 | − scopeConfig: ExecutorScopeConfigBackend; | |
| 77 | − scopeState: ExecutorScopeStateBackend; | |
| 78 | − sourceArtifacts: ExecutorSourceArtifactBackend; | |
| 79 | − auth: ExecutorAuthBackend; | |
| 80 | − secrets: ExecutorSecretsBackend; | |
| 81 | − executions: ExecutorExecutionsBackend; | |
| 82 | − close?: () => Promise<void>; | |
| 80 | +export type ExecutorWorkspaceRepository = { | |
| 81 | + config: ExecutorWorkspaceConfigRepository; | |
| 82 | + state: ExecutorWorkspaceStateRepository; | |
| 83 | + sourceArtifacts: ExecutorWorkspaceSourceArtifactRepository; | |
| 84 | + sourceAuth: ExecutorWorkspaceSourceAuthRepository; | |
| 85 | + localTools?: ExecutorWorkspaceLocalToolRepository; | |
| 86 | + sourceTypeDeclarations?: ExecutorWorkspaceSourceTypeDeclarationsRepository; | |
| 83 | 87 | }; |
| 84 | 88 | |
| 85 | −export type ExecutorBackendServices = { | |
| 89 | +export type ExecutorBackendRepositories = { | |
| 86 | 90 | scope: ExecutorScopeDescriptor; |
| 87 | − storage: ExecutorStorageBackend; | |
| 88 | − instanceConfig: ExecutorInstanceConfigBackend; | |
| 89 | − localTools?: ExecutorLocalToolBackend; | |
| 90 | − sourceTypeDeclarations?: ExecutorSourceTypeDeclarationsBackend; | |
| 91 | + installation: ExecutorInstallationRepository; | |
| 92 | + workspace: ExecutorWorkspaceRepository; | |
| 93 | + secrets: ExecutorSecretRepository; | |
| 94 | + executions: ExecutorExecutionRepository; | |
| 95 | + instanceConfig: ExecutorInstanceConfigRepository; | |
| 96 | + close?: () => Promise<void>; | |
| 91 | 97 | }; |
| 92 | 98 | |
| 93 | 99 | const toError = (cause: unknown): Error => |
| @@ -118,14 +124,14 @@ const toOptionEffect = <T>( · 24 unmodified lines | ||
| 118 | 124 | ); |
| 119 | 125 | |
| 120 | 126 | const toInstallationBackend = ( |
| 121 | − input: ExecutorInstallationBackend, | |
| 127 | + input: ExecutorInstallationRepository, | |
| 122 | 128 | ): BoundInstallationStore => ({ |
| 123 | 129 | load: () => toEffect(input.load()), |
| 124 | 130 | getOrProvision: () => toEffect(input.getOrProvision()), |
| 125 | 131 | }); |
| 126 | 132 | |
| 127 | 133 | const toScopeConfigBackend = ( |
| 128 | − input: ExecutorScopeConfigBackend, | |
| 134 | + input: ExecutorWorkspaceConfigRepository, | |
| 129 | 135 | ): BoundScopeConfigStore => ({ |
| 130 | 136 | load: () => toEffect(input.load()), |
| 131 | 137 | writeProject: (config) => toEffect(input.writeProject(config)), |
| @@ -133,14 +139,14 @@ const toScopeConfigBackend = ( · 1 unmodified line | ||
| 133 | 139 | }); |
| 134 | 140 | |
| 135 | 141 | const toScopeStateBackend = ( |
| 136 | − input: ExecutorScopeStateBackend, | |
| 142 | + input: ExecutorWorkspaceStateRepository, | |
| 137 | 143 | ): BoundScopeStateStore => ({ |
| 138 | 144 | load: () => toEffect(input.load()), |
| 139 | 145 | write: (state) => toEffect(input.write(state)), |
| 140 | 146 | }); |
| 141 | 147 | |
| 142 | 148 | const toSourceArtifactBackend = ( |
| 143 | − input: ExecutorSourceArtifactBackend, | |
| 149 | + input: ExecutorWorkspaceSourceArtifactRepository, | |
| 144 | 150 | ): BoundSourceArtifactStore => ({ |
| 145 | 151 | build: input.build, |
| 146 | 152 | read: (sourceId) => toEffect(input.read(sourceId)), |
| @@ -149,19 +155,19 @@ const toSourceArtifactBackend = ( · 2 unmodified lines | ||
| 149 | 155 | }); |
| 150 | 156 | |
| 151 | 157 | const toInstanceConfigBackend = ( |
| 152 | − input: ExecutorInstanceConfigBackend, | |
| 158 | + input: ExecutorInstanceConfigRepository, | |
| 153 | 159 | ): RuntimeInstanceConfigService => ({ |
| 154 | 160 | resolve: () => toEffect(input.resolve()), |
| 155 | 161 | }); |
| 156 | 162 | |
| 157 | 163 | const toLocalToolBackend = ( |
| 158 | − input: ExecutorLocalToolBackend, | |
| 164 | + input: ExecutorWorkspaceLocalToolRepository, | |
| 159 | 165 | ): BoundLocalToolRuntimeLoader => ({ |
| 160 | 166 | load: () => toEffect(input.load()), |
| 161 | 167 | }); |
| 162 | 168 | |
| 163 | 169 | const toSourceTypeDeclarationsBackend = ( |
| 164 | − input: ExecutorSourceTypeDeclarationsBackend, | |
| 170 | + input: ExecutorWorkspaceSourceTypeDeclarationsRepository, | |
| 165 | 171 | ): BoundSourceTypeDeclarationsRefresher => ({ |
| 166 | 172 | refreshWorkspaceInBackground: (payload) => |
| 167 | 173 | toEffect(input.refreshWorkspaceInBackground(payload)).pipe(Effect.orDie), |
| @@ -170,7 +176,7 @@ const toSourceTypeDeclarationsBackend = ( · 2 unmodified lines | ||
| 170 | 176 | }); |
| 171 | 177 | |
| 172 | 178 | const toAuthBackend = ( |
| 173 | − input: ExecutorAuthBackend, | |
| 179 | + input: ExecutorWorkspaceSourceAuthRepository, | |
| 174 | 180 | ): RuntimeAuthStorageServices => ({ |
| 175 | 181 | artifacts: { |
| 176 | 182 | listByScopeId: (scopeId) => toEffect(input.artifacts.listByScopeId(scopeId)), |
| @@ -230,7 +236,7 @@ const toAuthBackend = ( · 53 unmodified lines | ||
| 230 | 236 | }); |
| 231 | 237 | |
| 232 | 238 | const toSecretsBackend = ( |
| 233 | − input: ExecutorSecretsBackend, | |
| 239 | + input: ExecutorSecretRepository, | |
| 234 | 240 | ): RuntimeSecretsStorageServices => ({ |
| 235 | 241 | getById: (id) => toOptionEffect(input.getById(id)), |
| 236 | 242 | listAll: () => toEffect(input.listAll()), |
| @@ -244,7 +250,7 @@ const toSecretsBackend = ( · 7 unmodified lines | ||
| 244 | 250 | }); |
| 245 | 251 | |
| 246 | 252 | const toExecutionsBackend = ( |
| 247 | − input: ExecutorExecutionsBackend, | |
| 253 | + input: ExecutorExecutionRepository, | |
| 248 | 254 | ): RuntimeExecutionStorageServices => ({ |
| 249 | 255 | runs: { |
| 250 | 256 | getById: (executionId) => toOptionEffect(input.runs.getById(executionId)), |
| @@ -285,33 +291,37 @@ const toExecutionsBackend = ( · 34 unmodified lines | ||
| 285 | 291 | }); |
| 286 | 292 | |
| 287 | 293 | export const createExecutorBackend = (input: { |
| 288 | − loadServices: ( | |
| 294 | + loadRepositories: ( | |
| 289 | 295 | options: ExecutorRuntimeOptions, |
| 290 | − ) => MaybeEffect<ExecutorBackendServices>; | |
| 296 | + ) => MaybeEffect<ExecutorBackendRepositories>; | |
| 291 | 297 | }): ExecutorBackend => ({ |
| 292 | 298 | createRuntime: (options) => |
| 293 | − Effect.flatMap(toEffect(input.loadServices(options)), (services) => | |
| 299 | + Effect.flatMap(toEffect(input.loadRepositories(options)), (repositories) => | |
| 294 | 300 | createExecutorRuntimeFromServices({ |
| 295 | 301 | ...options, |
| 296 | 302 | services: { |
| 297 | − scope: services.scope, | |
| 303 | + scope: repositories.scope, | |
| 298 | 304 | storage: { |
| 299 | − installation: toInstallationBackend(services.storage.installation), | |
| 300 | − scopeConfig: toScopeConfigBackend(services.storage.scopeConfig), | |
| 301 | − scopeState: toScopeStateBackend(services.storage.scopeState), | |
| 302 | − sourceArtifacts: toSourceArtifactBackend(services.storage.sourceArtifacts), | |
| 303 | − auth: toAuthBackend(services.storage.auth), | |
| 304 | − secrets: toSecretsBackend(services.storage.secrets), | |
| 305 | − executions: toExecutionsBackend(services.storage.executions), | |
| 306 | − close: services.storage.close, | |
| 305 | + installation: toInstallationBackend(repositories.installation), | |
| 306 | + scopeConfig: toScopeConfigBackend(repositories.workspace.config), | |
| 307 | + scopeState: toScopeStateBackend(repositories.workspace.state), | |
| 308 | + sourceArtifacts: toSourceArtifactBackend( | |
| 309 | + repositories.workspace.sourceArtifacts, | |
| 310 | + ), | |
| 311 | + auth: toAuthBackend(repositories.workspace.sourceAuth), | |
| 312 | + secrets: toSecretsBackend(repositories.secrets), | |
| 313 | + executions: toExecutionsBackend(repositories.executions), | |
| 314 | + close: repositories.close, | |
| 307 | 315 | } satisfies RuntimeStorageServices, |
| 308 | − localToolRuntimeLoader: services.localTools | |
| 309 | − ? toLocalToolBackend(services.localTools) | |
| 316 | + localToolRuntimeLoader: repositories.workspace.localTools | |
| 317 | + ? toLocalToolBackend(repositories.workspace.localTools) | |
| 310 | 318 | : undefined, |
| 311 | − sourceTypeDeclarationsRefresher: services.sourceTypeDeclarations | |
| 312 | − ? toSourceTypeDeclarationsBackend(services.sourceTypeDeclarations) | |
| 319 | + sourceTypeDeclarationsRefresher: repositories.workspace.sourceTypeDeclarations | |
| 320 | + ? toSourceTypeDeclarationsBackend( | |
| 321 | + repositories.workspace.sourceTypeDeclarations, | |
| 322 | + ) | |
| 313 | 323 | : undefined, |
| 314 | − instanceConfig: toInstanceConfigBackend(services.instanceConfig), | |
| 324 | + instanceConfig: toInstanceConfigBackend(repositories.instanceConfig), | |
| 315 | 325 | }, |
| 316 | 326 | }), |
| 317 | 327 | ), |
| @@ -40,7 +40,7 @@ import { · 39 unmodified lines | ||
| 40 | 40 | ScopeIdSchema, |
| 41 | 41 | } from "./schema"; |
| 42 | 42 | import { |
| 43 | − createLocalExecutorServicesEffect, | |
| 43 | + createLocalExecutorRepositoriesEffect, | |
| 44 | 44 | } from "../../sdk-file/src/effect"; |
| 45 | 45 | import { |
| 46 | 46 | createExecutorApiLayer, |
| @@ -50,7 +50,7 @@ import { · 3 unmodified lines | ||
| 50 | 50 | } from "../../api/src/index"; |
| 51 | 51 | |
| 52 | 52 | type LocalExecutorServices = Effect.Effect.Success< |
| 53 | − ReturnType<typeof createLocalExecutorServicesEffect> | |
| 53 | + ReturnType<typeof createLocalExecutorRepositoriesEffect> | |
| 54 | 54 | >; |
| 55 | 55 | |
| 56 | 56 | const createClientLayer = (executor: Executor) => |
| @@ -168,7 +168,7 @@ const createScopedExecutor = (input: { · 111 unmodified lines | ||
| 168 | 168 | }) => |
| 169 | 169 | createExecutorEffect({ |
| 170 | 170 | backend: createExecutorBackend({ |
| 171 | − loadServices: () => | |
| 171 | + loadRepositories: () => | |
| 172 | 172 | Effect.succeed({ |
| 173 | 173 | ...input.services, |
| 174 | 174 | scope: { |
| @@ -176,12 +176,9 @@ const createScopedExecutor = (input: { · 1 unmodified line | ||
| 176 | 176 | actorScopeId: input.installation.actorScopeId, |
| 177 | 177 | resolutionScopeIds: input.installation.resolutionScopeIds, |
| 178 | 178 | }, |
| 179 | − storage: { | |
| 180 | − ...input.services.storage, | |
| 181 | − installation: { | |
| 182 | − load: () => Effect.succeed(input.installation), | |
| 183 | − getOrProvision: () => Effect.succeed(input.installation), | |
| 184 | − }, | |
| 179 | + installation: { | |
| 180 | + load: () => Effect.succeed(input.installation), | |
| 181 | + getOrProvision: () => Effect.succeed(input.installation), | |
| 185 | 182 | }, |
| 186 | 183 | }), |
| 187 | 184 | }), |
| @@ -200,21 +197,21 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 12 unmodified lines | ||
| 200 | 197 | prefix: "executor-sdk-scope-b-", |
| 201 | 198 | }); |
| 202 | 199 | |
| 203 | − const servicesA = yield* createLocalExecutorServicesEffect({ | |
| 200 | + const servicesA = yield* createLocalExecutorRepositoriesEffect({ | |
| 204 | 201 | workspaceRoot: workspaceRootA, |
| 205 | 202 | homeConfigPath: join(workspaceRootA, ".executor-home.jsonc"), |
| 206 | 203 | homeStateDirectory: join(workspaceRootA, ".executor-home-state"), |
| 207 | 204 | }); |
| 208 | − const servicesB = yield* createLocalExecutorServicesEffect({ | |
| 205 | + const servicesB = yield* createLocalExecutorRepositoriesEffect({ | |
| 209 | 206 | workspaceRoot: workspaceRootB, |
| 210 | 207 | homeConfigPath: join(workspaceRootB, ".executor-home.jsonc"), |
| 211 | 208 | homeStateDirectory: join(workspaceRootB, ".executor-home-state"), |
| 212 | 209 | }); |
| 213 | 210 | |
| 214 | 211 | const baseInstallationA = |
| 215 | − yield* servicesA.storage.installation.getOrProvision(); | |
| 212 | + yield* servicesA.installation.getOrProvision(); | |
| 216 | 213 | const baseInstallationB = |
| 217 | − yield* servicesB.storage.installation.getOrProvision(); | |
| 214 | + yield* servicesB.installation.getOrProvision(); | |
| 218 | 215 | |
| 219 | 216 | const sharedStorageClose = (() => { |
| 220 | 217 | let closed = false; |
| @@ -223,14 +220,12 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 2 unmodified lines | ||
| 223 | 220 | return; |
| 224 | 221 | } |
| 225 | 222 | closed = true; |
| 226 | − await servicesA.storage.close?.(); | |
| 227 | − await servicesB.storage.close?.(); | |
| 223 | + await servicesA.close?.(); | |
| 224 | + await servicesB.close?.(); | |
| 228 | 225 | }; |
| 229 | 226 | })(); |
| 230 | 227 | |
| 231 | 228 | const sharedStorage = { |
| 232 | − executorState: servicesA.storage.executorState, | |
| 233 | − secretMaterial: servicesA.storage.secretMaterial, | |
| 234 | 229 | close: sharedStorageClose, |
| 235 | 230 | }; |
| 236 | 231 | |
| @@ -256,20 +251,14 @@ const makeSharedScopeExecutors = Effect.acquireRelease( · 19 unmodified lines | ||
| 256 | 251 | const executorA = yield* createScopedExecutor({ |
| 257 | 252 | services: { |
| 258 | 253 | ...servicesA, |
| 259 | − storage: { | |
| 260 | − ...servicesA.storage, | |
| 261 | − ...sharedStorage, | |
| 262 | − }, | |
| 254 | + ...sharedStorage, | |
| 263 | 255 | }, |
| 264 | 256 | installation: installationA, |
| 265 | 257 | }); |
| 266 | 258 | const executorB = yield* createScopedExecutor({ |
| 267 | 259 | services: { |
| 268 | 260 | ...servicesB, |
| 269 | − storage: { | |
| 270 | − ...servicesB.storage, | |
| 271 | − ...sharedStorage, | |
| 272 | − }, | |
| 261 | + ...sharedStorage, | |
| 273 | 262 | }, |
| 274 | 263 | installation: installationB, |
| 275 | 264 | }); |
| @@ -369,7 +369,7 @@ describe("execution-mcp-resume", () => { · 368 unmodified lines | ||
| 369 | 369 | expect(created.pendingInteraction.payloadJson).toContain("Approve gated echo"); |
| 370 | 370 | } |
| 371 | 371 | |
| 372 | − const pendingInteraction = yield* runtime.storage.executorState.executionInteractions.getPendingByExecutionId( | |
| 372 | + const pendingInteraction = yield* runtime.storage.executions.interactions.getPendingByExecutionId( | |
| 373 | 373 | created.execution.id, |
| 374 | 374 | ); |
| 375 | 375 | |
| @@ -59,6 +59,7 @@ import { · 58 unmodified lines | ||
| 59 | 59 | |
| 60 | 60 | import { |
| 61 | 61 | type ExecutorRuntime, |
| 62 | + type ExecutorStateStoreShape, | |
| 62 | 63 | LiveExecutionManagerService, |
| 63 | 64 | provideExecutorRuntime, |
| 64 | 65 | RuntimeExecutionResolverService, |
| @@ -346,7 +347,7 @@ const upsertLocalSecret = ( · 281 unmodified lines | ||
| 346 | 347 | value: string; |
| 347 | 348 | }, |
| 348 | 349 | ) => |
| 349 | − runtime.storage.executorState.secretMaterials.upsert({ | |
| 350 | + executorStateForRuntime(runtime).secretMaterials.upsert({ | |
| 350 | 351 | id: SecretMaterialIdSchema.make(input.id), |
| 351 | 352 | providerId: "local", |
| 352 | 353 | handle: input.id, |
| @@ -357,6 +358,21 @@ const upsertLocalSecret = ( · 4 unmodified lines | ||
| 357 | 358 | updatedAt: Date.now(), |
| 358 | 359 | }); |
| 359 | 360 | |
| 361 | +const executorStateForRuntime = ( | |
| 362 | + runtime: ExecutorRuntime, | |
| 363 | +): ExecutorStateStoreShape => ({ | |
| 364 | + authArtifacts: runtime.storage.auth.artifacts, | |
| 365 | + authLeases: runtime.storage.auth.leases, | |
| 366 | + sourceOauthClients: runtime.storage.auth.sourceOauthClients, | |
| 367 | + scopeOauthClients: runtime.storage.auth.scopeOauthClients, | |
| 368 | + providerAuthGrants: runtime.storage.auth.providerGrants, | |
| 369 | + sourceAuthSessions: runtime.storage.auth.sourceSessions, | |
| 370 | + secretMaterials: runtime.storage.secrets, | |
| 371 | + executions: runtime.storage.executions.runs, | |
| 372 | + executionInteractions: runtime.storage.executions.interactions, | |
| 373 | + executionSteps: runtime.storage.executions.steps, | |
| 374 | +}); | |
| 375 | + | |
| 360 | 376 | const createPersistedGoogleSource = (input: { |
| 361 | 377 | runtime: ExecutorRuntime; |
| 362 | 378 | name: string; |
| @@ -403,7 +419,7 @@ const createPersistedGoogleSource = (input: { · 40 unmodified lines | ||
| 403 | 419 | now: Date.now(), |
| 404 | 420 | }); |
| 405 | 421 | |
| 406 | − return yield* persistSource(input.runtime.storage.executorState, source, { | |
| 422 | + return yield* persistSource(executorStateForRuntime(input.runtime), source, { | |
| 407 | 423 | actorScopeId: installation.actorScopeId, |
| 408 | 424 | }).pipe((effect) => provideExecutorRuntime(effect, input.runtime)); |
| 409 | 425 | }); |
| @@ -563,7 +579,7 @@ describe("executor-runtime", () => { · 153 unmodified lines | ||
| 563 | 579 | expect(updated.name).toBe("GitHub PAT Updated"); |
| 564 | 580 | |
| 565 | 581 | const storedSecret = |
| 566 | − yield* runtime.storage.executorState.secretMaterials.getById( | |
| 582 | + yield* executorStateForRuntime(runtime).secretMaterials.getById( | |
| 567 | 583 | SecretMaterialIdSchema.make(created.id), |
| 568 | 584 | ); |
| 569 | 585 | assertTrue(Option.isSome(storedSecret)); |
| @@ -798,7 +814,7 @@ describe("executor-runtime", () => { · 228 unmodified lines | ||
| 798 | 814 | ); |
| 799 | 815 | const now = Date.now(); |
| 800 | 816 | |
| 801 | − yield* runtime.storage.executorState.executions.insert({ | |
| 817 | + yield* executorStateForRuntime(runtime).executions.insert({ | |
| 802 | 818 | id: executionId, |
| 803 | 819 | scopeId: installation.scopeId, |
| 804 | 820 | createdByScopeId: installation.actorScopeId, |
| @@ -833,7 +849,7 @@ describe("executor-runtime", () => { · 28 unmodified lines | ||
| 833 | 849 | }, |
| 834 | 850 | now, |
| 835 | 851 | }).pipe(Effect.orDie); |
| 836 | − yield* persistSource(runtime.storage.executorState, localSource, { | |
| 852 | + yield* persistSource(executorStateForRuntime(runtime), localSource, { | |
| 837 | 853 | actorScopeId: installation.actorScopeId, |
| 838 | 854 | }).pipe( |
| 839 | 855 | (effect) => provideExecutorRuntime(effect, runtime), |
| @@ -843,7 +859,7 @@ describe("executor-runtime", () => { · 3 unmodified lines | ||
| 843 | 859 | const interactionFiber = yield* Effect.gen(function* () { |
| 844 | 860 | const liveExecutionManager = yield* LiveExecutionManagerService; |
| 845 | 861 | const onElicitation = liveExecutionManager.createOnElicitation({ |
| 846 | − executorState: runtime.storage.executorState, | |
| 862 | + executorState: executorStateForRuntime(runtime), | |
| 847 | 863 | executionId, |
| 848 | 864 | }); |
| 849 | 865 | |
| @@ -874,7 +890,7 @@ describe("executor-runtime", () => { · 24 unmodified lines | ||
| 874 | 890 | const waitForPendingInteraction = ( |
| 875 | 891 | remaining: number, |
| 876 | 892 | ): Effect.Effect<Option.Option<ExecutionInteraction>, Error> => |
| 877 | − runtime.storage.executorState.executionInteractions | |
| 893 | + executorStateForRuntime(runtime).executionInteractions | |
| 878 | 894 | .getPendingByExecutionId(executionId) |
| 879 | 895 | .pipe( |
| 880 | 896 | Effect.flatMap((pendingInteraction) => |
| @@ -947,7 +963,7 @@ describe("executor-runtime", () => { · 66 unmodified lines | ||
| 947 | 963 | : "", |
| 948 | 964 | ); |
| 949 | 965 | const storedSecret = |
| 950 | − yield* runtime.storage.executorState.secretMaterials.getById( | |
| 966 | + yield* executorStateForRuntime(runtime).secretMaterials.getById( | |
| 951 | 967 | tokenSecretMaterialId, |
| 952 | 968 | ); |
| 953 | 969 | assertTrue(Option.isSome(storedSecret)); |
| @@ -963,7 +979,7 @@ describe("executor-runtime", () => { · 9 unmodified lines | ||
| 963 | 979 | ); |
| 964 | 980 | |
| 965 | 981 | const storedInteraction = |
| 966 | − yield* runtime.storage.executorState.executionInteractions.getById( | |
| 982 | + yield* executorStateForRuntime(runtime).executionInteractions.getById( | |
| 967 | 983 | pendingInteraction.value.id, |
| 968 | 984 | ); |
| 969 | 985 | assertTrue(Option.isSome(storedInteraction)); |
| @@ -1315,7 +1331,7 @@ describe("executor-runtime", () => { · 345 unmodified lines | ||
| 1315 | 1331 | }, |
| 1316 | 1332 | now, |
| 1317 | 1333 | }).pipe(Effect.orDie); |
| 1318 | − yield* persistSource(runtime.storage.executorState, localSource, { | |
| 1334 | + yield* persistSource(executorStateForRuntime(runtime), localSource, { | |
| 1319 | 1335 | actorScopeId: installation.actorScopeId, |
| 1320 | 1336 | }).pipe( |
| 1321 | 1337 | (effect) => provideExecutorRuntime(effect, runtime), |
| @@ -1373,7 +1389,7 @@ describe("executor-runtime", () => { · 51 unmodified lines | ||
| 1373 | 1389 | }, |
| 1374 | 1390 | now, |
| 1375 | 1391 | }).pipe(Effect.orDie); |
| 1376 | − yield* persistSource(runtime.storage.executorState, localSource, { | |
| 1392 | + yield* persistSource(executorStateForRuntime(runtime), localSource, { | |
| 1377 | 1393 | actorScopeId: installation.actorScopeId, |
| 1378 | 1394 | }).pipe( |
| 1379 | 1395 | (effect) => provideExecutorRuntime(effect, runtime), |
| @@ -1415,7 +1431,7 @@ describe("executor-runtime", () => { · 35 unmodified lines | ||
| 1415 | 1431 | ); |
| 1416 | 1432 | const now = Date.now(); |
| 1417 | 1433 | |
| 1418 | − yield* runtime.storage.executorState.executions.insert({ | |
| 1434 | + yield* executorStateForRuntime(runtime).executions.insert({ | |
| 1419 | 1435 | id: executionId, |
| 1420 | 1436 | scopeId: installation.scopeId, |
| 1421 | 1437 | createdByScopeId: installation.actorScopeId, |
| @@ -1450,7 +1466,7 @@ describe("executor-runtime", () => { · 28 unmodified lines | ||
| 1450 | 1466 | }, |
| 1451 | 1467 | now, |
| 1452 | 1468 | }).pipe(Effect.orDie); |
| 1453 | − yield* persistSource(runtime.storage.executorState, localSource, { | |
| 1469 | + yield* persistSource(executorStateForRuntime(runtime), localSource, { | |
| 1454 | 1470 | actorScopeId: installation.actorScopeId, |
| 1455 | 1471 | }).pipe( |
| 1456 | 1472 | (effect) => provideExecutorRuntime(effect, runtime), |
| @@ -1460,7 +1476,7 @@ describe("executor-runtime", () => { · 3 unmodified lines | ||
| 1460 | 1476 | const interactionFiber = yield* Effect.gen(function* () { |
| 1461 | 1477 | const liveExecutionManager = yield* LiveExecutionManagerService; |
| 1462 | 1478 | const onElicitation = liveExecutionManager.createOnElicitation({ |
| 1463 | − executorState: runtime.storage.executorState, | |
| 1479 | + executorState: executorStateForRuntime(runtime), | |
| 1464 | 1480 | executionId, |
| 1465 | 1481 | }); |
| 1466 | 1482 | |
| @@ -1491,7 +1507,7 @@ describe("executor-runtime", () => { · 24 unmodified lines | ||
| 1491 | 1507 | const waitForPendingInteraction = ( |
| 1492 | 1508 | remaining: number, |
| 1493 | 1509 | ): Effect.Effect<Option.Option<ExecutionInteraction>, Error> => |
| 1494 | − runtime.storage.executorState.executionInteractions | |
| 1510 | + executorStateForRuntime(runtime).executionInteractions | |
| 1495 | 1511 | .getPendingByExecutionId(executionId) |
| 1496 | 1512 | .pipe( |
| 1497 | 1513 | Effect.flatMap((pendingInteraction) => |
| @@ -1522,7 +1538,7 @@ describe("executor-runtime", () => { · 24 unmodified lines | ||
| 1522 | 1538 | }); |
| 1523 | 1539 | |
| 1524 | 1540 | const storedInteraction = |
| 1525 | − yield* runtime.storage.executorState.executionInteractions.getById( | |
| 1541 | + yield* executorStateForRuntime(runtime).executionInteractions.getById( | |
| 1526 | 1542 | pendingInteraction.value.id, |
| 1527 | 1543 | ); |
| 1528 | 1544 | assertTrue(Option.isSome(storedInteraction)); |
| @@ -1615,7 +1631,7 @@ describe("executor-runtime", () => { · 86 unmodified lines | ||
| 1615 | 1631 | const grantId = ProviderAuthGrantIdSchema.make( |
| 1616 | 1632 | `provider_grant_${crypto.randomUUID()}`, |
| 1617 | 1633 | ); |
| 1618 | − yield* runtime.storage.executorState.providerAuthGrants.upsert({ | |
| 1634 | + yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({ | |
| 1619 | 1635 | id: grantId, |
| 1620 | 1636 | scopeId: installation.scopeId, |
| 1621 | 1637 | actorScopeId: installation.actorScopeId, |
| @@ -1677,7 +1693,7 @@ describe("executor-runtime", () => { · 55 unmodified lines | ||
| 1677 | 1693 | }); |
| 1678 | 1694 | |
| 1679 | 1695 | const storedGrant = |
| 1680 | − yield* runtime.storage.executorState.providerAuthGrants.getById(grantId); | |
| 1696 | + yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId); | |
| 1681 | 1697 | assertTrue(Option.isSome(storedGrant)); |
| 1682 | 1698 | expect(storedGrant.value.orphanedAt).toBeNull(); |
| 1683 | 1699 | expect(googleServer.discoveryAuthorizations).toContain( |
| @@ -1791,7 +1807,7 @@ describe("executor-runtime", () => { · 107 unmodified lines | ||
| 1791 | 1807 | const grantId = ProviderAuthGrantIdSchema.make( |
| 1792 | 1808 | `provider_grant_${crypto.randomUUID()}`, |
| 1793 | 1809 | ); |
| 1794 | − yield* runtime.storage.executorState.providerAuthGrants.upsert({ | |
| 1810 | + yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({ | |
| 1795 | 1811 | id: grantId, |
| 1796 | 1812 | scopeId: installation.scopeId, |
| 1797 | 1813 | actorScopeId: installation.actorScopeId, |
| @@ -1870,7 +1886,7 @@ describe("executor-runtime", () => { · 72 unmodified lines | ||
| 1870 | 1886 | ).toBe(true); |
| 1871 | 1887 | |
| 1872 | 1888 | const storedGrant = |
| 1873 | − yield* runtime.storage.executorState.providerAuthGrants.getById(grantId); | |
| 1889 | + yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId); | |
| 1874 | 1890 | assertTrue(Option.isSome(storedGrant)); |
| 1875 | 1891 | expect(storedGrant.value.orphanedAt).toBeNull(); |
| 1876 | 1892 | expect(googleServer.tokenRequests.length).toBeGreaterThanOrEqual(1); |
| @@ -2001,7 +2017,7 @@ describe("executor-runtime", () => { · 124 unmodified lines | ||
| 2001 | 2017 | const grantId = ProviderAuthGrantIdSchema.make( |
| 2002 | 2018 | `provider_grant_${crypto.randomUUID()}`, |
| 2003 | 2019 | ); |
| 2004 | − yield* runtime.storage.executorState.providerAuthGrants.upsert({ | |
| 2020 | + yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({ | |
| 2005 | 2021 | id: grantId, |
| 2006 | 2022 | scopeId: installation.scopeId, |
| 2007 | 2023 | actorScopeId: installation.actorScopeId, |
| @@ -2026,7 +2042,7 @@ describe("executor-runtime", () => { · 18 unmodified lines | ||
| 2026 | 2042 | `src_auth_${crypto.randomUUID()}`, |
| 2027 | 2043 | ); |
| 2028 | 2044 | const state = `provider-state-${crypto.randomUUID()}`; |
| 2029 | − yield* runtime.storage.executorState.sourceAuthSessions.upsert({ | |
| 2045 | + yield* executorStateForRuntime(runtime).sourceAuthSessions.upsert({ | |
| 2030 | 2046 | id: sessionId, |
| 2031 | 2047 | scopeId: installation.scopeId, |
| 2032 | 2048 | sourceId: SourceIdSchema.make( |
| @@ -2097,7 +2113,7 @@ describe("executor-runtime", () => { · 64 unmodified lines | ||
| 2097 | 2113 | expect(callbackPage).toContain("Connected 1 source"); |
| 2098 | 2114 | |
| 2099 | 2115 | const storedGrant = |
| 2100 | − yield* runtime.storage.executorState.providerAuthGrants.getById(grantId); | |
| 2116 | + yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId); | |
| 2101 | 2117 | assertTrue(Option.isSome(storedGrant)); |
| 2102 | 2118 | expect(storedGrant.value.refreshToken).toEqual({ |
| 2103 | 2119 | providerId: "local", |
| @@ -2155,7 +2171,7 @@ describe("executor-runtime", () => { · 51 unmodified lines | ||
| 2155 | 2171 | ); |
| 2156 | 2172 | const refreshSecretId = "sec_google_refresh_orphan"; |
| 2157 | 2173 | |
| 2158 | − yield* runtime.storage.executorState.scopeOauthClients.upsert({ | |
| 2174 | + yield* executorStateForRuntime(runtime).scopeOauthClients.upsert({ | |
| 2159 | 2175 | id: oauthClientId, |
| 2160 | 2176 | scopeId: installation.scopeId, |
| 2161 | 2177 | providerKey: "google_workspace", |
| @@ -2177,7 +2193,7 @@ describe("executor-runtime", () => { · 15 unmodified lines | ||
| 2177 | 2193 | const grantId = ProviderAuthGrantIdSchema.make( |
| 2178 | 2194 | `provider_grant_${crypto.randomUUID()}`, |
| 2179 | 2195 | ); |
| 2180 | − yield* runtime.storage.executorState.providerAuthGrants.upsert({ | |
| 2196 | + yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({ | |
| 2181 | 2197 | id: grantId, |
| 2182 | 2198 | scopeId: installation.scopeId, |
| 2183 | 2199 | actorScopeId: installation.actorScopeId, |
| @@ -2228,7 +2244,7 @@ describe("executor-runtime", () => { · 44 unmodified lines | ||
| 2228 | 2244 | expect(removed.removed).toBe(true); |
| 2229 | 2245 | |
| 2230 | 2246 | const storedGrant = |
| 2231 | − yield* runtime.storage.executorState.providerAuthGrants.getById(grantId); | |
| 2247 | + yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId); | |
| 2232 | 2248 | assertTrue(Option.isSome(storedGrant)); |
| 2233 | 2249 | expect(storedGrant.value.orphanedAt).not.toBeNull(); |
| 2234 | 2250 | }), |
| @@ -2245,7 +2261,7 @@ describe("executor-runtime", () => { · 10 unmodified lines | ||
| 2245 | 2261 | ); |
| 2246 | 2262 | const refreshSecretId = "sec_google_refresh_revoke"; |
| 2247 | 2263 | |
| 2248 | − yield* runtime.storage.executorState.scopeOauthClients.upsert({ | |
| 2264 | + yield* executorStateForRuntime(runtime).scopeOauthClients.upsert({ | |
| 2249 | 2265 | id: oauthClientId, |
| 2250 | 2266 | scopeId: installation.scopeId, |
| 2251 | 2267 | providerKey: "google_workspace", |
| @@ -2267,7 +2283,7 @@ describe("executor-runtime", () => { · 15 unmodified lines | ||
| 2267 | 2283 | const grantId = ProviderAuthGrantIdSchema.make( |
| 2268 | 2284 | `provider_grant_${crypto.randomUUID()}`, |
| 2269 | 2285 | ); |
| 2270 | − yield* runtime.storage.executorState.providerAuthGrants.upsert({ | |
| 2286 | + yield* executorStateForRuntime(runtime).providerAuthGrants.upsert({ | |
| 2271 | 2287 | id: grantId, |
| 2272 | 2288 | scopeId: installation.scopeId, |
| 2273 | 2289 | actorScopeId: installation.actorScopeId, |
| @@ -2335,11 +2351,11 @@ describe("executor-runtime", () => { · 61 unmodified lines | ||
| 2335 | 2351 | expect(removed.removed).toBe(true); |
| 2336 | 2352 | |
| 2337 | 2353 | const storedGrant = |
| 2338 | − yield* runtime.storage.executorState.providerAuthGrants.getById(grantId); | |
| 2354 | + yield* executorStateForRuntime(runtime).providerAuthGrants.getById(grantId); | |
| 2339 | 2355 | assertTrue(Option.isNone(storedGrant)); |
| 2340 | 2356 | |
| 2341 | 2357 | const storedRefreshSecret = |
| 2342 | − yield* runtime.storage.executorState.secretMaterials.getById( | |
| 2358 | + yield* executorStateForRuntime(runtime).secretMaterials.getById( | |
| 2343 | 2359 | SecretMaterialIdSchema.make(refreshSecretId), |
| 2344 | 2360 | ); |
| 2345 | 2361 | assertTrue(Option.isNone(storedRefreshSecret)); |
| @@ -0,0 +1,26 @@ | ||
| 1 | +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | |
| 2 | +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | |
| 3 | +import { z } from "zod/v4"; | |
| 4 | + | |
| 5 | +const server = new McpServer( | |
| 6 | + { name: "mcp-stdio-test-server", version: "1.0.0" }, | |
| 7 | + { capabilities: { tools: { listChanged: true } } }, | |
| 8 | +); | |
| 9 | + | |
| 10 | +server.registerTool( | |
| 11 | + "echo_stdio", | |
| 12 | + { | |
| 13 | + title: "Echo Stdio", | |
| 14 | + description: "Echo a value over stdio", | |
| 15 | + inputSchema: { | |
| 16 | + value: z.string(), | |
| 17 | + }, | |
| 18 | + }, | |
| 19 | + async ({ value }) => ({ | |
| 20 | + content: [{ type: "text", text: "stdio:" + value }], | |
| 21 | + }), | |
| 22 | +); | |
| 23 | + | |
| 24 | +const transport = new StdioServerTransport(); | |
| 25 | +await server.connect(transport); | |
| 26 | +setInterval(() => {}, 1 << 30); | |
| @@ -1,6 +1,9 @@ | ||
| 1 | 1 | import { |
| 2 | 2 | randomUUID, |
| 3 | 3 | } from "node:crypto"; |
| 4 | +import { | |
| 5 | + fileURLToPath, | |
| 6 | +} from "node:url"; | |
| 4 | 7 | |
| 5 | 8 | import { |
| 6 | 9 | createMcpExpressApp, |
| @@ -70,6 +73,19 @@ type RealMcpServer = { · 63 unmodified lines | ||
| 70 | 73 | close: () => Promise<void>; |
| 71 | 74 | }; |
| 72 | 75 | |
| 76 | +const PACKAGE_ROOT = fileURLToPath(new URL("../../../../", import.meta.url)); | |
| 77 | +const STDIO_SERVER_SCRIPT_PATH = fileURLToPath( | |
| 78 | + new URL("./__fixtures__/mcp-stdio-server.mjs", import.meta.url), | |
| 79 | +); | |
| 80 | +const BUN_COMMAND = | |
| 81 | + process.env.BUN_INSTALL && process.env.BUN_INSTALL.length > 0 | |
| 82 | + ? `${process.env.BUN_INSTALL}/bin/bun` | |
| 83 | + : "bun"; | |
| 84 | +const STDIO_TEST_ENV = { | |
| 85 | + PATH: process.env.PATH ?? "", | |
| 86 | + HOME: process.env.HOME ?? "", | |
| 87 | +}; | |
| 88 | + | |
| 73 | 89 | const makeLoadedCatalog = (input: { |
| 74 | 90 | source: Source; |
| 75 | 91 | snapshot: CatalogSnapshotV1; |
| @@ -567,40 +583,6 @@ const makeStatefulMcpServer = Effect.acquireRelease( · 491 unmodified lines | ||
| 567 | 583 | }).pipe(Effect.orDie), |
| 568 | 584 | ); |
| 569 | 585 | |
| 570 | −const STDIO_TEST_SERVER_SCRIPT = ` | |
| 571 | −import { | |
| 572 | − McpServer, | |
| 573 | −} from "@modelcontextprotocol/sdk/server/mcp.js"; | |
| 574 | −import { | |
| 575 | − StdioServerTransport, | |
| 576 | −} from "@modelcontextprotocol/sdk/server/stdio.js"; | |
| 577 | −import { | |
| 578 | − z, | |
| 579 | −} from "zod/v4"; | |
| 580 | − | |
| 581 | −const server = new McpServer( | |
| 582 | − { name: "mcp-stdio-test-server", version: "1.0.0" }, | |
| 583 | − { capabilities: { tools: { listChanged: true } } }, | |
| 584 | −); | |
| 585 | − | |
| 586 | −server.registerTool( | |
| 587 | − "echo_stdio", | |
| 588 | − { | |
| 589 | − title: "Echo Stdio", | |
| 590 | − description: "Echo a value over stdio", | |
| 591 | − inputSchema: { | |
| 592 | − value: z.string(), | |
| 593 | − }, | |
| 594 | − }, | |
| 595 | − async ({ value }) => ({ | |
| 596 | − content: [{ type: "text", text: "stdio:" + value }], | |
| 597 | − }), | |
| 598 | −); | |
| 599 | − | |
| 600 | −const transport = new StdioServerTransport(); | |
| 601 | −await server.connect(transport); | |
| 602 | −`; | |
| 603 | − | |
| 604 | 586 | const makeStaticAuthProvider = (accessToken: string): OAuthClientProvider => ({ |
| 605 | 587 | get redirectUrl() { |
| 606 | 588 | return "http://127.0.0.1/oauth/callback"; |
| @@ -1020,10 +1002,10 @@ describe("mcp source adapter", () => { · 413 unmodified lines | ||
| 1020 | 1002 | transport: "stdio", |
| 1021 | 1003 | queryParams: null, |
| 1022 | 1004 | headers: null, |
| 1023 | − command: "node", | |
| 1024 | − args: ["--input-type=module", "-e", STDIO_TEST_SERVER_SCRIPT], | |
| 1025 | − env: null, | |
| 1026 | − cwd: process.cwd(), | |
| 1005 | + command: BUN_COMMAND, | |
| 1006 | + args: [STDIO_SERVER_SCRIPT_PATH], | |
| 1007 | + env: STDIO_TEST_ENV, | |
| 1008 | + cwd: PACKAGE_ROOT, | |
| 1027 | 1009 | }, |
| 1028 | 1010 | importAuthPolicy: "reuse_runtime", |
| 1029 | 1011 | importAuth: { kind: "none" }, |
| @@ -13,7 +13,7 @@ import * as Effect from "effect/Effect"; · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | import { |
| 15 | 15 | discoverSource, |
| 16 | −} from "./source-discovery"; | |
| 16 | +} from "../../sources/discovery"; | |
| 17 | 17 | |
| 18 | 18 | type TestServer = { |
| 19 | 19 | url: string; |
| @@ -10,7 +10,7 @@ import type { · 9 unmodified lines | ||
| 10 | 10 | } from "../catalog/source/runtime"; |
| 11 | 11 | import { |
| 12 | 12 | inspectionToolDetailFromTool, |
| 13 | −} from "./source-inspection"; | |
| 13 | +} from "../../sources/inspection"; | |
| 14 | 14 | |
| 15 | 15 | describe("source inspection", () => { |
| 16 | 16 | it.effect("keeps the shared TypeScript contract first-class on tool detail", () => |
| @@ -462,7 +462,9 @@ export const mcpSourceAdapter = { · 461 unmodified lines | ||
| 462 | 462 | Effect.mapError( |
| 463 | 463 | (cause) => |
| 464 | 464 | new Error( |
| 465 | − `Failed discovering MCP tools for ${source.id}: ${cause.message}`, | |
| 465 | + `Failed discovering MCP tools for ${source.id}: ${cause.message}${ | |
| 466 | + cause.details ? ` (${cause.details})` : "" | |
| 467 | + }`, | |
| 466 | 468 | ), |
| 467 | 469 | ), |
| 468 | 470 | ); |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; |
| 2 | 2 | import { Client } from "@modelcontextprotocol/sdk/client/index.js"; |
| 3 | 3 | import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; |
| 4 | +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; | |
| 4 | 5 | import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; |
| 5 | 6 | import * as Data from "effect/Data"; |
| 6 | 7 | import * as Effect from "effect/Effect"; |
| @@ -104,31 +105,6 @@ const closeClient = (client: Client): Effect.Effect<void, never, never> => · 97 unmodified lines | ||
| 104 | 105 | }), |
| 105 | 106 | }).pipe(Effect.ignore); |
| 106 | 107 | |
| 107 | −type DynamicImport = <TModule>(specifier: string) => Promise<TModule>; | |
| 108 | − | |
| 109 | −const dynamicImport = new Function( | |
| 110 | − "specifier", | |
| 111 | − "return import(specifier)", | |
| 112 | −) as DynamicImport; | |
| 113 | − | |
| 114 | −const loadStdioClientTransport = (): Effect.Effect< | |
| 115 | − typeof import("@modelcontextprotocol/sdk/client/stdio.js").StdioClientTransport, | |
| 116 | − McpConnectionError, | |
| 117 | − never | |
| 118 | −> => | |
| 119 | − Effect.tryPromise({ | |
| 120 | − try: async () => | |
| 121 | − (await dynamicImport<typeof import("@modelcontextprotocol/sdk/client/stdio.js")>( | |
| 122 | − "@modelcontextprotocol/sdk/client/stdio.js", | |
| 123 | − )).StdioClientTransport, | |
| 124 | − catch: (cause) => | |
| 125 | − mcpConnectionError({ | |
| 126 | − transport: "stdio", | |
| 127 | − message: "Failed loading MCP stdio transport", | |
| 128 | − cause, | |
| 129 | − }), | |
| 130 | − }); | |
| 131 | − | |
| 132 | 108 | const connectClient = (input: { |
| 133 | 109 | createClient: () => Client; |
| 134 | 110 | transport: McpTransportPreference; |
| @@ -143,7 +119,10 @@ const connectClient = (input: { · 8 unmodified lines | ||
| 143 | 119 | catch: (cause) => |
| 144 | 120 | mcpConnectionError({ |
| 145 | 121 | transport: input.transport, |
| 146 | − message: `Failed connecting to MCP server via ${input.transport}`, | |
| 122 | + message: | |
| 123 | + `Failed connecting to MCP server via ${input.transport}: ${ | |
| 124 | + cause instanceof Error ? cause.message : String(cause) | |
| 125 | + }`, | |
| 147 | 126 | cause, |
| 148 | 127 | }), |
| 149 | 128 | }).pipe( |
| @@ -183,7 +162,6 @@ export const createSdkMcpConnector = ( · 33 unmodified lines | ||
| 183 | 162 | }); |
| 184 | 163 | } |
| 185 | 164 | |
| 186 | − const StdioClientTransport = yield* loadStdioClientTransport(); | |
| 187 | 165 | return yield* connectClient({ |
| 188 | 166 | createClient, |
| 189 | 167 | transport: "stdio", |