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