Unified · Split
| @@ -92,14 +92,14 @@ const writeConfiguredLocalMcpSource = (input: { · 91 unmodified lines | ||
| 92 | 92 | name?: string; |
| 93 | 93 | namespace?: string; |
| 94 | 94 | }) => |
| 95 | − Effect.promise(async () => { | |
| 95 | + Effect.gen(function* () { | |
| 96 | 96 | const sourceId = SourceIdSchema.make(input.configKey); |
| 97 | − const context = await resolveLocalWorkspaceContext({ | |
| 97 | + const context = yield* resolveLocalWorkspaceContext({ | |
| 98 | 98 | workspaceRoot: input.workspaceRoot, |
| 99 | 99 | }); |
| 100 | 100 | const installation = deriveLocalInstallation(context); |
| 101 | 101 | |
| 102 | − await writeProjectLocalExecutorConfig({ | |
| 102 | + yield* writeProjectLocalExecutorConfig({ | |
| 103 | 103 | context, |
| 104 | 104 | config: { |
| 105 | 105 | sources: { |
| @@ -162,7 +162,7 @@ const writeConfiguredLocalMcpSource = (input: { · 56 unmodified lines | ||
| 162 | 162 | }], |
| 163 | 163 | }); |
| 164 | 164 | |
| 165 | − await writeLocalSourceArtifact({ | |
| 165 | + yield* writeLocalSourceArtifact({ | |
| 166 | 166 | context, |
| 167 | 167 | sourceId, |
| 168 | 168 | artifact: buildLocalSourceArtifact({ |
| @@ -718,6 +718,7 @@ describe("local-executor-server", () => { · 549 unmodified lines | ||
| 718 | 718 | expect(execution.execution.status).toBe("completed"); |
| 719 | 719 | expect(execution.execution.resultJson).toBe(JSON.stringify({ sum: 42 })); |
| 720 | 720 | }), |
| 721 | + 15_000, | |
| 721 | 722 | ); |
| 722 | 723 | |
| 723 | 724 | it.scoped("includes completed MCP return values in text content", () => |
| @@ -193,13 +193,10 @@ export const createControlPlaneRuntime = ( · 192 unmodified lines | ||
| 193 | 193 | const persistence = Context.get(baseContext, SqlControlPlanePersistenceService); |
| 194 | 194 | const rows = Context.get(baseContext, SqlControlPlaneRowsService); |
| 195 | 195 | |
| 196 | − const localWorkspaceContext = yield* Effect.tryPromise({ | |
| 197 | − try: () => | |
| 198 | − resolveLocalWorkspaceContext({ | |
| 199 | − workspaceRoot: options.workspaceRoot, | |
| 200 | − }), | |
| 201 | − catch: toLocalRuntimeBootstrapError, | |
| 196 | + const localWorkspaceContext = yield* resolveLocalWorkspaceContext({ | |
| 197 | + workspaceRoot: options.workspaceRoot, | |
| 202 | 198 | }).pipe( |
| 199 | + Effect.mapError(toLocalRuntimeBootstrapError), | |
| 203 | 200 | Effect.catchAll((error) => |
| 204 | 201 | closeScope(scope).pipe( |
| 205 | 202 | Effect.zipRight(Effect.fail(error)), |
| @@ -217,10 +214,8 @@ export const createControlPlaneRuntime = ( · 11 unmodified lines | ||
| 217 | 214 | )), |
| 218 | 215 | ); |
| 219 | 216 | |
| 220 | − const loadedLocalConfig = yield* Effect.tryPromise({ | |
| 221 | − try: () => loadLocalExecutorConfig(localWorkspaceContext), | |
| 222 | − catch: toLocalRuntimeBootstrapError, | |
| 223 | − }).pipe( | |
| 217 | + const loadedLocalConfig = yield* loadLocalExecutorConfig(localWorkspaceContext).pipe( | |
| 218 | + Effect.mapError(toLocalRuntimeBootstrapError), | |
| 224 | 219 | Effect.catchAll((error) => |
| 225 | 220 | closeScope(scope).pipe( |
| 226 | 221 | Effect.zipRight(Effect.fail(error)), |
| @@ -43,12 +43,8 @@ describe("local-config", () => { · 42 unmodified lines | ||
| 43 | 43 | ), |
| 44 | 44 | ); |
| 45 | 45 | |
| 46 | − const context = yield* Effect.promise(() => | |
| 47 | − resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 48 | − ); | |
| 49 | − const loaded = yield* Effect.promise(() => | |
| 50 | − loadLocalExecutorConfig(context), | |
| 51 | − ); | |
| 46 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 47 | + const loaded = yield* loadLocalExecutorConfig(context); | |
| 52 | 48 | |
| 53 | 49 | expect(loaded.config?.sources?.github?.kind).toBe("openapi"); |
| 54 | 50 | expect(loaded.config?.sources?.github?.connection.endpoint).toBe( |
| @@ -80,16 +76,8 @@ describe("local-config", () => { · 25 unmodified lines | ||
| 80 | 76 | ), |
| 81 | 77 | ); |
| 82 | 78 | |
| 83 | − const context = yield* Effect.promise(() => | |
| 84 | − resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 85 | − ); | |
| 86 | − const failure = yield* Effect.flip( | |
| 87 | − Effect.tryPromise({ | |
| 88 | − try: () => loadLocalExecutorConfig(context), | |
| 89 | − catch: (cause) => | |
| 90 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 91 | − }), | |
| 92 | − ); | |
| 79 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 80 | + const failure = yield* Effect.flip(loadLocalExecutorConfig(context)); | |
| 93 | 81 | |
| 94 | 82 | expect(failure.message).toContain("Invalid executor config"); |
| 95 | 83 | expect(failure.message).toContain("line 5, column 7"); |
| @@ -128,13 +116,11 @@ describe("local-config", () => { · 32 unmodified lines | ||
| 128 | 116 | ), |
| 129 | 117 | ); |
| 130 | 118 | |
| 131 | − const resolvedPath = yield* Effect.promise(() => | |
| 132 | − resolveHomeConfigPath({ | |
| 133 | − platform: "darwin", | |
| 134 | − homeDirectory, | |
| 135 | − env: {}, | |
| 136 | − }), | |
| 137 | − ); | |
| 119 | + const resolvedPath = yield* resolveHomeConfigPath({ | |
| 120 | + platform: "darwin", | |
| 121 | + homeDirectory, | |
| 122 | + env: {}, | |
| 123 | + }); | |
| 138 | 124 | |
| 139 | 125 | expect(resolvedPath).toBe(join(legacyConfigDirectory, "executor.jsonc")); |
| 140 | 126 | }), |
| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | import { createHash } from "node:crypto"; |
| 2 | 2 | import { homedir } from "node:os"; |
| 3 | 3 | import { basename, dirname, isAbsolute, join, resolve } from "node:path"; |
| 4 | −import { promises as fs } from "node:fs"; | |
| 4 | +import { FileSystem } from "@effect/platform"; | |
| 5 | +import { NodeFileSystem } from "@effect/platform-node"; | |
| 5 | 6 | import { |
| 6 | 7 | type ParseError as JsoncParseError, |
| 7 | 8 | parse as parseJsoncDocument, |
| @@ -15,6 +16,7 @@ import { · 7 unmodified lines | ||
| 15 | 16 | type LocalConfigSecretProvider, |
| 16 | 17 | type LocalConfigSource, |
| 17 | 18 | } from "#schema"; |
| 19 | +import * as Effect from "effect/Effect"; | |
| 18 | 20 | import * as Schema from "effect/Schema"; |
| 19 | 21 | |
| 20 | 22 | const decodeLocalExecutorConfig = Schema.decodeUnknownSync(LocalExecutorConfigSchema); |
| @@ -30,22 +32,18 @@ const normalizeSlashPath = (value: string): string => · 9 unmodified lines | ||
| 30 | 32 | const stableHash = (value: string): string => |
| 31 | 33 | createHash("sha256").update(value).digest("hex").slice(0, 16); |
| 32 | 34 | |
| 33 | −const fileExists = async (path: string): Promise<boolean> => { | |
| 34 | − try { | |
| 35 | − await fs.access(path); | |
| 36 | − return true; | |
| 37 | − } catch { | |
| 38 | − return false; | |
| 39 | − } | |
| 40 | −}; | |
| 41 | − | |
| 42 | −const pathExists = async (path: string): Promise<boolean> => { | |
| 43 | − try { | |
| 44 | − await fs.access(path); | |
| 45 | − return true; | |
| 46 | − } catch { | |
| 47 | − return false; | |
| 48 | − } | |
| 35 | +const provideNodeFileSystem = <A, E, R>( | |
| 36 | + effect: Effect.Effect<A, E, R | FileSystem.FileSystem>, | |
| 37 | +): Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem>> => | |
| 38 | + effect.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect< | |
| 39 | + A, | |
| 40 | + E, | |
| 41 | + Exclude<R, FileSystem.FileSystem> | |
| 42 | + >; | |
| 43 | + | |
| 44 | +const mapFileSystemError = (path: string, action: string) => (cause: unknown): Error => { | |
| 45 | + const message = cause instanceof Error ? cause.message : String(cause); | |
| 46 | + return new Error(`Failed to ${action} ${path}: ${message}`); | |
| 49 | 47 | }; |
| 50 | 48 | |
| 51 | 49 | const trimOrUndefined = (value: string | undefined | null): string | undefined => { |
| @@ -111,21 +109,26 @@ export const resolveDefaultHomeConfigCandidates = (input: { · 59 unmodified lines | ||
| 111 | 109 | ]); |
| 112 | 110 | }; |
| 113 | 111 | |
| 114 | −export const resolveHomeConfigPath = async (input: { | |
| 112 | +export const resolveHomeConfigPath = (input: { | |
| 115 | 113 | env?: NodeJS.ProcessEnv; |
| 116 | 114 | platform?: NodeJS.Platform; |
| 117 | 115 | homeDirectory?: string; |
| 118 | −} = {}): Promise<string> => { | |
| 119 | − const candidates = resolveDefaultHomeConfigCandidates(input); | |
| 120 | − | |
| 121 | − for (const candidate of candidates) { | |
| 122 | − if (await fileExists(candidate)) { | |
| 123 | − return candidate; | |
| 116 | +} = {}): Effect.Effect<string, Error> => | |
| 117 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 118 | + const fs = yield* FileSystem.FileSystem; | |
| 119 | + const candidates = resolveDefaultHomeConfigCandidates(input); | |
| 120 | + | |
| 121 | + for (const candidate of candidates) { | |
| 122 | + const exists = yield* fs.exists(candidate).pipe( | |
| 123 | + Effect.mapError(mapFileSystemError(candidate, "check config path")), | |
| 124 | + ); | |
| 125 | + if (exists) { | |
| 126 | + return candidate; | |
| 127 | + } | |
| 124 | 128 | } |
| 125 | − } | |
| 126 | 129 | |
| 127 | − return candidates[0]!; | |
| 128 | −}; | |
| 130 | + return candidates[0]!; | |
| 131 | + })); | |
| 129 | 132 | |
| 130 | 133 | const formatJsoncParseErrors = (content: string, errors: readonly JsoncParseError[]): string => { |
| 131 | 134 | const lines = content.split("\n"); |
| @@ -233,47 +236,74 @@ export const mergeLocalExecutorConfigs = ( · 101 unmodified lines | ||
| 233 | 236 | }); |
| 234 | 237 | }; |
| 235 | 238 | |
| 236 | −const resolveProjectConfigPath = async (workspaceRoot: string): Promise<string> => { | |
| 237 | − const jsoncPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME); | |
| 238 | − if (await fileExists(jsoncPath)) { | |
| 239 | − return jsoncPath; | |
| 240 | − } | |
| 241 | − | |
| 242 | − const jsonPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_FALLBACK_BASENAME); | |
| 243 | − if (await fileExists(jsonPath)) { | |
| 244 | − return jsonPath; | |
| 245 | − } | |
| 246 | − | |
| 247 | − return jsoncPath; | |
| 248 | −}; | |
| 239 | +const resolveProjectConfigPathEffect = (workspaceRoot: string) => | |
| 240 | + Effect.gen(function* () { | |
| 241 | + const fs = yield* FileSystem.FileSystem; | |
| 242 | + const jsoncPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME); | |
| 243 | + const jsoncExists = yield* fs.exists(jsoncPath).pipe( | |
| 244 | + Effect.mapError(mapFileSystemError(jsoncPath, "check project config path")), | |
| 245 | + ); | |
| 246 | + if (jsoncExists) { | |
| 247 | + return jsoncPath; | |
| 248 | + } | |
| 249 | 249 | |
| 250 | −const hasProjectConfig = async (workspaceRoot: string): Promise<boolean> => | |
| 251 | − fileExists(join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME)) | |
| 252 | − || fileExists(join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_FALLBACK_BASENAME)); | |
| 250 | + const jsonPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_FALLBACK_BASENAME); | |
| 251 | + const jsonExists = yield* fs.exists(jsonPath).pipe( | |
| 252 | + Effect.mapError(mapFileSystemError(jsonPath, "check project config path")), | |
| 253 | + ); | |
| 254 | + return jsonExists ? jsonPath : jsoncPath; | |
| 255 | + }); | |
| 253 | 256 | |
| 254 | −const resolveWorkspaceRootFromCwd = async (cwd: string): Promise<string> => { | |
| 255 | − let current = resolve(cwd); | |
| 256 | − let nearestProjectConfigRoot: string | null = null; | |
| 257 | − let nearestGitRoot: string | null = null; | |
| 257 | +const hasProjectConfigEffect = (workspaceRoot: string) => | |
| 258 | + Effect.gen(function* () { | |
| 259 | + const fs = yield* FileSystem.FileSystem; | |
| 260 | + const jsoncPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_BASENAME); | |
| 261 | + const jsonPath = join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, PROJECT_CONFIG_FALLBACK_BASENAME); | |
| 262 | + const [jsoncExists, jsonExists] = yield* Effect.all([ | |
| 263 | + fs.exists(jsoncPath).pipe( | |
| 264 | + Effect.mapError(mapFileSystemError(jsoncPath, "check project config path")), | |
| 265 | + ), | |
| 266 | + fs.exists(jsonPath).pipe( | |
| 267 | + Effect.mapError(mapFileSystemError(jsonPath, "check project config path")), | |
| 268 | + ), | |
| 269 | + ]); | |
| 270 | + return jsoncExists || jsonExists; | |
| 271 | + }); | |
| 258 | 272 | |
| 259 | − while (true) { | |
| 260 | − if (nearestProjectConfigRoot === null && (await hasProjectConfig(current))) { | |
| 261 | − nearestProjectConfigRoot = current; | |
| 262 | − } | |
| 273 | +const resolveWorkspaceRootFromCwdEffect = (cwd: string) => | |
| 274 | + Effect.gen(function* () { | |
| 275 | + const fs = yield* FileSystem.FileSystem; | |
| 276 | + let current = resolve(cwd); | |
| 277 | + let nearestProjectConfigRoot: string | null = null; | |
| 278 | + let nearestGitRoot: string | null = null; | |
| 279 | + | |
| 280 | + while (true) { | |
| 281 | + if (nearestProjectConfigRoot === null) { | |
| 282 | + const hasProjectConfig = yield* hasProjectConfigEffect(current); | |
| 283 | + if (hasProjectConfig) { | |
| 284 | + nearestProjectConfigRoot = current; | |
| 285 | + } | |
| 286 | + } | |
| 263 | 287 | |
| 264 | − if (nearestGitRoot === null && (await pathExists(join(current, ".git")))) { | |
| 265 | − nearestGitRoot = current; | |
| 266 | − } | |
| 288 | + if (nearestGitRoot === null) { | |
| 289 | + const gitPath = join(current, ".git"); | |
| 290 | + const gitExists = yield* fs.exists(gitPath).pipe( | |
| 291 | + Effect.mapError(mapFileSystemError(gitPath, "check git root")), | |
| 292 | + ); | |
| 293 | + if (gitExists) { | |
| 294 | + nearestGitRoot = current; | |
| 295 | + } | |
| 296 | + } | |
| 267 | 297 | |
| 268 | − const parent = dirname(current); | |
| 269 | − if (parent === current) { | |
| 270 | − break; | |
| 298 | + const parent = dirname(current); | |
| 299 | + if (parent === current) { | |
| 300 | + break; | |
| 301 | + } | |
| 302 | + current = parent; | |
| 271 | 303 | } |
| 272 | − current = parent; | |
| 273 | − } | |
| 274 | 304 | |
| 275 | − return nearestProjectConfigRoot ?? nearestGitRoot ?? resolve(cwd); | |
| 276 | −}; | |
| 305 | + return nearestProjectConfigRoot ?? nearestGitRoot ?? resolve(cwd); | |
| 306 | + }); | |
| 277 | 307 | |
| 278 | 308 | export type ResolvedLocalWorkspaceContext = { |
| 279 | 309 | cwd: string; |
| @@ -295,73 +325,91 @@ export type LoadedLocalExecutorConfig = { · 15 unmodified lines | ||
| 295 | 325 | projectConfigPath: string; |
| 296 | 326 | }; |
| 297 | 327 | |
| 298 | −export const resolveLocalWorkspaceContext = async (input: { | |
| 328 | +export const resolveLocalWorkspaceContext = (input: { | |
| 299 | 329 | cwd?: string; |
| 300 | 330 | workspaceRoot?: string; |
| 301 | −} = {}): Promise<ResolvedLocalWorkspaceContext> => { | |
| 302 | − const cwd = resolve(input.cwd ?? process.cwd()); | |
| 303 | − const workspaceRoot = resolve( | |
| 304 | − input.workspaceRoot ?? (await resolveWorkspaceRootFromCwd(cwd)), | |
| 305 | − ); | |
| 306 | − const workspaceName = basename(workspaceRoot) || "workspace"; | |
| 307 | − const projectConfigPath = await resolveProjectConfigPath(workspaceRoot); | |
| 308 | − const homeConfigPath = await resolveHomeConfigPath(); | |
| 309 | − | |
| 310 | − return { | |
| 311 | − cwd, | |
| 312 | − workspaceRoot, | |
| 313 | − workspaceName, | |
| 314 | − configDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY), | |
| 315 | − projectConfigPath, | |
| 316 | − homeConfigPath, | |
| 317 | − artifactsDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "artifacts"), | |
| 318 | − stateDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "state"), | |
| 319 | − installationId: `local_${stableHash(normalizeSlashPath(workspaceRoot))}`, | |
| 320 | − }; | |
| 321 | −}; | |
| 322 | − | |
| 323 | −export const readOptionalLocalExecutorConfig = async ( | |
| 331 | +} = {}): Effect.Effect<ResolvedLocalWorkspaceContext, Error> => | |
| 332 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 333 | + const cwd = resolve(input.cwd ?? process.cwd()); | |
| 334 | + const workspaceRoot = resolve( | |
| 335 | + input.workspaceRoot ?? (yield* resolveWorkspaceRootFromCwdEffect(cwd)), | |
| 336 | + ); | |
| 337 | + const workspaceName = basename(workspaceRoot) || "workspace"; | |
| 338 | + const projectConfigPath = yield* resolveProjectConfigPathEffect(workspaceRoot); | |
| 339 | + const homeConfigPath = yield* resolveHomeConfigPath(); | |
| 340 | + | |
| 341 | + return { | |
| 342 | + cwd, | |
| 343 | + workspaceRoot, | |
| 344 | + workspaceName, | |
| 345 | + configDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY), | |
| 346 | + projectConfigPath, | |
| 347 | + homeConfigPath, | |
| 348 | + artifactsDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "artifacts"), | |
| 349 | + stateDirectory: join(workspaceRoot, PROJECT_CONFIG_DIRECTORY, "state"), | |
| 350 | + installationId: `local_${stableHash(normalizeSlashPath(workspaceRoot))}`, | |
| 351 | + }; | |
| 352 | + })); | |
| 353 | + | |
| 354 | +export const readOptionalLocalExecutorConfig = ( | |
| 324 | 355 | path: string, |
| 325 | −): Promise<LocalExecutorConfig | null> => { | |
| 326 | − if (!(await fileExists(path))) { | |
| 327 | − return null; | |
| 328 | − } | |
| 356 | +): Effect.Effect<LocalExecutorConfig | null, Error> => | |
| 357 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 358 | + const fs = yield* FileSystem.FileSystem; | |
| 359 | + const exists = yield* fs.exists(path).pipe( | |
| 360 | + Effect.mapError(mapFileSystemError(path, "check config path")), | |
| 361 | + ); | |
| 362 | + if (!exists) { | |
| 363 | + return null; | |
| 364 | + } | |
| 329 | 365 | |
| 330 | − const content = await fs.readFile(path, "utf8"); | |
| 331 | − return parseJsonc({ path, content }); | |
| 332 | −}; | |
| 366 | + const content = yield* fs.readFileString(path, "utf8").pipe( | |
| 367 | + Effect.mapError(mapFileSystemError(path, "read config")), | |
| 368 | + ); | |
| 369 | + return yield* Effect.try({ | |
| 370 | + try: () => parseJsonc({ path, content }), | |
| 371 | + catch: (cause) => | |
| 372 | + cause instanceof Error ? cause : new Error(String(cause)), | |
| 373 | + }); | |
| 374 | + })); | |
| 333 | 375 | |
| 334 | −export const loadLocalExecutorConfig = async ( | |
| 376 | +export const loadLocalExecutorConfig = ( | |
| 335 | 377 | context: ResolvedLocalWorkspaceContext, |
| 336 | −): Promise<LoadedLocalExecutorConfig> => { | |
| 337 | − const [homeConfig, projectConfig] = await Promise.all([ | |
| 338 | − readOptionalLocalExecutorConfig(context.homeConfigPath), | |
| 339 | − readOptionalLocalExecutorConfig(context.projectConfigPath), | |
| 340 | − ]); | |
| 341 | − | |
| 342 | − return { | |
| 343 | − config: mergeLocalExecutorConfigs(homeConfig, projectConfig), | |
| 344 | − homeConfig, | |
| 345 | − projectConfig, | |
| 346 | − homeConfigPath: context.homeConfigPath, | |
| 347 | − projectConfigPath: context.projectConfigPath, | |
| 348 | − }; | |
| 349 | −}; | |
| 378 | +): Effect.Effect<LoadedLocalExecutorConfig, Error> => | |
| 379 | + Effect.gen(function* () { | |
| 380 | + const [homeConfig, projectConfig] = yield* Effect.all([ | |
| 381 | + readOptionalLocalExecutorConfig(context.homeConfigPath), | |
| 382 | + readOptionalLocalExecutorConfig(context.projectConfigPath), | |
| 383 | + ]); | |
| 384 | + | |
| 385 | + return { | |
| 386 | + config: mergeLocalExecutorConfigs(homeConfig, projectConfig), | |
| 387 | + homeConfig, | |
| 388 | + projectConfig, | |
| 389 | + homeConfigPath: context.homeConfigPath, | |
| 390 | + projectConfigPath: context.projectConfigPath, | |
| 391 | + }; | |
| 392 | + }); | |
| 350 | 393 | |
| 351 | 394 | export const encodeLocalExecutorConfig = (config: LocalExecutorConfig): string => |
| 352 | 395 | `${JSON.stringify(config, null, 2)}\n`; |
| 353 | 396 | |
| 354 | −export const writeProjectLocalExecutorConfig = async (input: { | |
| 397 | +export const writeProjectLocalExecutorConfig = (input: { | |
| 355 | 398 | context: ResolvedLocalWorkspaceContext; |
| 356 | 399 | config: LocalExecutorConfig; |
| 357 | −}): Promise<void> => { | |
| 358 | − await fs.mkdir(input.context.configDirectory, { recursive: true }); | |
| 359 | − await fs.writeFile( | |
| 360 | − input.context.projectConfigPath, | |
| 361 | − encodeLocalExecutorConfig(input.config), | |
| 362 | − "utf8", | |
| 363 | − ); | |
| 364 | −}; | |
| 400 | +}): Effect.Effect<void, Error> => | |
| 401 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 402 | + const fs = yield* FileSystem.FileSystem; | |
| 403 | + yield* fs.makeDirectory(input.context.configDirectory, { recursive: true }).pipe( | |
| 404 | + Effect.mapError(mapFileSystemError(input.context.configDirectory, "create config directory")), | |
| 405 | + ); | |
| 406 | + yield* fs.writeFileString( | |
| 407 | + input.context.projectConfigPath, | |
| 408 | + encodeLocalExecutorConfig(input.config), | |
| 409 | + ).pipe( | |
| 410 | + Effect.mapError(mapFileSystemError(input.context.projectConfigPath, "write config")), | |
| 411 | + ); | |
| 412 | + })); | |
| 365 | 413 | |
| 366 | 414 | export const resolveConfigRelativePath = (input: { |
| 367 | 415 | path: string; |
| @@ -36,9 +36,9 @@ describe("local-installation", () => { · 35 unmodified lines | ||
| 36 | 36 | it.scoped("is idempotent when loading the default local installation", () => |
| 37 | 37 | Effect.gen(function* () { |
| 38 | 38 | const runtime = yield* makeRuntime; |
| 39 | − const context = yield* Effect.promise(() => | |
| 40 | − resolveLocalWorkspaceContext({ workspaceRoot: TEST_WORKSPACE_ROOT }), | |
| 41 | − ); | |
| 39 | + const context = yield* resolveLocalWorkspaceContext({ | |
| 40 | + workspaceRoot: TEST_WORKSPACE_ROOT, | |
| 41 | + }); | |
| 42 | 42 | |
| 43 | 43 | const first = runtime.localInstallation; |
| 44 | 44 | const second = yield* getOrProvisionLocalInstallation({ |
| @@ -185,7 +185,14 @@ export const getLocalInstallation = () => · 184 unmodified lines | ||
| 185 | 185 | const store = yield* ControlPlaneStore; |
| 186 | 186 | const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); |
| 187 | 187 | const context = runtimeLocalWorkspace?.context |
| 188 | − ?? (yield* Effect.promise(() => resolveLocalWorkspaceContext())); | |
| 188 | + ?? (yield* resolveLocalWorkspaceContext().pipe( | |
| 189 | + Effect.mapError((error) => | |
| 190 | + localOps.installation.unknownStorage( | |
| 191 | + error, | |
| 192 | + "Failed resolving local workspace context", | |
| 193 | + ), | |
| 194 | + ), | |
| 195 | + )); | |
| 189 | 196 | |
| 190 | 197 | return yield* loadLocalInstallation(store, context); |
| 191 | 198 | }); |
| @@ -1,5 +1,6 @@ | ||
| 1 | −import { promises as fs } from "node:fs"; | |
| 2 | 1 | import { join } from "node:path"; |
| 2 | +import { FileSystem } from "@effect/platform"; | |
| 3 | +import { NodeFileSystem } from "@effect/platform-node"; | |
| 3 | 4 | |
| 4 | 5 | import { |
| 5 | 6 | SourceIdSchema, |
| @@ -12,6 +13,7 @@ import { · 6 unmodified lines | ||
| 12 | 13 | type Source, |
| 13 | 14 | type SourceRecipeId, |
| 14 | 15 | } from "#schema"; |
| 16 | +import * as Effect from "effect/Effect"; | |
| 15 | 17 | import * as Schema from "effect/Schema"; |
| 16 | 18 | |
| 17 | 19 | import type { ResolvedLocalWorkspaceContext } from "./local-config"; |
| @@ -41,6 +43,20 @@ export type LocalSourceArtifact = typeof LocalSourceArtifactSchema.Type; · 23 unmodified lines | ||
| 41 | 43 | |
| 42 | 44 | const decodeLocalSourceArtifact = Schema.decodeUnknownSync(LocalSourceArtifactSchema); |
| 43 | 45 | |
| 46 | +const provideNodeFileSystem = <A, E, R>( | |
| 47 | + effect: Effect.Effect<A, E, R | FileSystem.FileSystem>, | |
| 48 | +): Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem>> => | |
| 49 | + effect.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect< | |
| 50 | + A, | |
| 51 | + E, | |
| 52 | + Exclude<R, FileSystem.FileSystem> | |
| 53 | + >; | |
| 54 | + | |
| 55 | +const mapFileSystemError = (path: string, action: string) => (cause: unknown): Error => { | |
| 56 | + const message = cause instanceof Error ? cause.message : String(cause); | |
| 57 | + return new Error(`Failed to ${action} ${path}: ${message}`); | |
| 58 | +}; | |
| 59 | + | |
| 44 | 60 | const localSourceArtifactPath = (input: { |
| 45 | 61 | context: ResolvedLocalWorkspaceContext; |
| 46 | 62 | sourceId: string; |
| @@ -138,58 +154,63 @@ export const buildLocalSourceArtifact = (input: { · 91 unmodified lines | ||
| 138 | 154 | }; |
| 139 | 155 | }; |
| 140 | 156 | |
| 141 | −export const readLocalSourceArtifact = async (input: { | |
| 157 | +export const readLocalSourceArtifact = (input: { | |
| 142 | 158 | context: ResolvedLocalWorkspaceContext; |
| 143 | 159 | sourceId: string; |
| 144 | −}): Promise<LocalSourceArtifact | null> => { | |
| 145 | − const path = localSourceArtifactPath(input); | |
| 146 | − | |
| 147 | − try { | |
| 148 | − const content = await fs.readFile(path, "utf8"); | |
| 149 | − return decodeLocalSourceArtifact(JSON.parse(content) as unknown); | |
| 150 | − } catch (cause) { | |
| 151 | − if ( | |
| 152 | − cause instanceof Error | |
| 153 | − && ("code" in cause) | |
| 154 | − && (cause as NodeJS.ErrnoException).code === "ENOENT" | |
| 155 | − ) { | |
| 160 | +}): Effect.Effect<LocalSourceArtifact | null, Error> => | |
| 161 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 162 | + const fs = yield* FileSystem.FileSystem; | |
| 163 | + const path = localSourceArtifactPath(input); | |
| 164 | + const exists = yield* fs.exists(path).pipe( | |
| 165 | + Effect.mapError(mapFileSystemError(path, "check source artifact path")), | |
| 166 | + ); | |
| 167 | + if (!exists) { | |
| 156 | 168 | return null; |
| 157 | 169 | } |
| 158 | 170 | |
| 159 | − const message = cause instanceof Error ? cause.message : String(cause); | |
| 160 | − throw new Error(`Invalid local source artifact at ${path}: ${message}`); | |
| 161 | − } | |
| 162 | −}; | |
| 171 | + const content = yield* fs.readFileString(path, "utf8").pipe( | |
| 172 | + Effect.mapError(mapFileSystemError(path, "read source artifact")), | |
| 173 | + ); | |
| 174 | + return yield* Effect.try({ | |
| 175 | + try: () => decodeLocalSourceArtifact(JSON.parse(content) as unknown), | |
| 176 | + catch: (cause) => { | |
| 177 | + const message = cause instanceof Error ? cause.message : String(cause); | |
| 178 | + return new Error(`Invalid local source artifact at ${path}: ${message}`); | |
| 179 | + }, | |
| 180 | + }); | |
| 181 | + })); | |
| 163 | 182 | |
| 164 | −export const writeLocalSourceArtifact = async (input: { | |
| 183 | +export const writeLocalSourceArtifact = (input: { | |
| 165 | 184 | context: ResolvedLocalWorkspaceContext; |
| 166 | 185 | sourceId: string; |
| 167 | 186 | artifact: LocalSourceArtifact; |
| 168 | −}): Promise<void> => { | |
| 169 | − const path = localSourceArtifactPath(input); | |
| 170 | − await fs.mkdir(join(input.context.artifactsDirectory, "sources"), { | |
| 171 | − recursive: true, | |
| 172 | − }); | |
| 173 | − await fs.writeFile(path, `${JSON.stringify(input.artifact, null, 2)}\n`, "utf8"); | |
| 174 | −}; | |
| 187 | +}): Effect.Effect<void, Error> => | |
| 188 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 189 | + const fs = yield* FileSystem.FileSystem; | |
| 190 | + const directory = join(input.context.artifactsDirectory, "sources"); | |
| 191 | + const path = localSourceArtifactPath(input); | |
| 192 | + yield* fs.makeDirectory(directory, { recursive: true }).pipe( | |
| 193 | + Effect.mapError(mapFileSystemError(directory, "create source artifact directory")), | |
| 194 | + ); | |
| 195 | + yield* fs.writeFileString(path, `${JSON.stringify(input.artifact, null, 2)}\n`).pipe( | |
| 196 | + Effect.mapError(mapFileSystemError(path, "write source artifact")), | |
| 197 | + ); | |
| 198 | + })); | |
| 175 | 199 | |
| 176 | −export const removeLocalSourceArtifact = async (input: { | |
| 200 | +export const removeLocalSourceArtifact = (input: { | |
| 177 | 201 | context: ResolvedLocalWorkspaceContext; |
| 178 | 202 | sourceId: string; |
| 179 | −}): Promise<void> => { | |
| 180 | − const path = localSourceArtifactPath(input); | |
| 181 | − | |
| 182 | − try { | |
| 183 | − await fs.unlink(path); | |
| 184 | − } catch (cause) { | |
| 185 | − if ( | |
| 186 | − cause instanceof Error | |
| 187 | − && ("code" in cause) | |
| 188 | − && (cause as NodeJS.ErrnoException).code === "ENOENT" | |
| 189 | − ) { | |
| 203 | +}): Effect.Effect<void, Error> => | |
| 204 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 205 | + const fs = yield* FileSystem.FileSystem; | |
| 206 | + const path = localSourceArtifactPath(input); | |
| 207 | + const exists = yield* fs.exists(path).pipe( | |
| 208 | + Effect.mapError(mapFileSystemError(path, "check source artifact path")), | |
| 209 | + ); | |
| 210 | + if (!exists) { | |
| 190 | 211 | return; |
| 191 | 212 | } |
| 192 | − | |
| 193 | − throw cause; | |
| 194 | − } | |
| 195 | −}; | |
| 213 | + yield* fs.remove(path).pipe( | |
| 214 | + Effect.mapError(mapFileSystemError(path, "remove source artifact")), | |
| 215 | + ); | |
| 216 | + })); | |
| @@ -1,11 +1,13 @@ | ||
| 1 | −import { promises as fs } from "node:fs"; | |
| 2 | 1 | import { join } from "node:path"; |
| 2 | +import { FileSystem } from "@effect/platform"; | |
| 3 | +import { NodeFileSystem } from "@effect/platform-node"; | |
| 3 | 4 | |
| 4 | 5 | import { |
| 5 | 6 | PolicyIdSchema, |
| 6 | 7 | SourceStatusSchema, |
| 7 | 8 | TimestampMsSchema, |
| 8 | 9 | } from "#schema"; |
| 10 | +import * as Effect from "effect/Effect"; | |
| 9 | 11 | import * as Schema from "effect/Schema"; |
| 10 | 12 | |
| 11 | 13 | import type { ResolvedLocalWorkspaceContext } from "./local-config"; |
| @@ -44,6 +46,20 @@ export type LocalWorkspaceState = typeof LocalWorkspaceStateSchema.Type; · 32 unmodified lines | ||
| 44 | 46 | |
| 45 | 47 | const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalWorkspaceStateSchema); |
| 46 | 48 | |
| 49 | +const provideNodeFileSystem = <A, E, R>( | |
| 50 | + effect: Effect.Effect<A, E, R | FileSystem.FileSystem>, | |
| 51 | +): Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem>> => | |
| 52 | + effect.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect< | |
| 53 | + A, | |
| 54 | + E, | |
| 55 | + Exclude<R, FileSystem.FileSystem> | |
| 56 | + >; | |
| 57 | + | |
| 58 | +const mapFileSystemError = (path: string, action: string) => (cause: unknown): Error => { | |
| 59 | + const message = cause instanceof Error ? cause.message : String(cause); | |
| 60 | + return new Error(`Failed to ${action} ${path}: ${message}`); | |
| 61 | +}; | |
| 62 | + | |
| 47 | 63 | const defaultLocalWorkspaceState = (): LocalWorkspaceState => ({ |
| 48 | 64 | version: 1, |
| 49 | 65 | sources: {}, |
| @@ -54,36 +70,44 @@ export const localWorkspaceStatePath = ( · 4 unmodified lines | ||
| 54 | 70 | context: ResolvedLocalWorkspaceContext, |
| 55 | 71 | ): string => join(context.stateDirectory, WORKSPACE_STATE_BASENAME); |
| 56 | 72 | |
| 57 | −export const loadLocalWorkspaceState = async ( | |
| 73 | +export const loadLocalWorkspaceState = ( | |
| 58 | 74 | context: ResolvedLocalWorkspaceContext, |
| 59 | −): Promise<LocalWorkspaceState> => { | |
| 60 | − const path = localWorkspaceStatePath(context); | |
| 61 | − | |
| 62 | − try { | |
| 63 | − const content = await fs.readFile(path, "utf8"); | |
| 64 | − return decodeLocalWorkspaceState(JSON.parse(content) as unknown); | |
| 65 | − } catch (cause) { | |
| 66 | − if ( | |
| 67 | − cause instanceof Error | |
| 68 | − && ("code" in cause) | |
| 69 | − && (cause as NodeJS.ErrnoException).code === "ENOENT" | |
| 70 | − ) { | |
| 75 | +): Effect.Effect<LocalWorkspaceState, Error> => | |
| 76 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 77 | + const fs = yield* FileSystem.FileSystem; | |
| 78 | + const path = localWorkspaceStatePath(context); | |
| 79 | + const exists = yield* fs.exists(path).pipe( | |
| 80 | + Effect.mapError(mapFileSystemError(path, "check workspace state path")), | |
| 81 | + ); | |
| 82 | + if (!exists) { | |
| 71 | 83 | return defaultLocalWorkspaceState(); |
| 72 | 84 | } |
| 73 | 85 | |
| 74 | − const message = cause instanceof Error ? cause.message : String(cause); | |
| 75 | − throw new Error(`Invalid local workspace state at ${path}: ${message}`); | |
| 76 | − } | |
| 77 | −}; | |
| 86 | + const content = yield* fs.readFileString(path, "utf8").pipe( | |
| 87 | + Effect.mapError(mapFileSystemError(path, "read workspace state")), | |
| 88 | + ); | |
| 89 | + return yield* Effect.try({ | |
| 90 | + try: () => decodeLocalWorkspaceState(JSON.parse(content) as unknown), | |
| 91 | + catch: (cause) => { | |
| 92 | + const message = cause instanceof Error ? cause.message : String(cause); | |
| 93 | + return new Error(`Invalid local workspace state at ${path}: ${message}`); | |
| 94 | + }, | |
| 95 | + }); | |
| 96 | + })); | |
| 78 | 97 | |
| 79 | −export const writeLocalWorkspaceState = async (input: { | |
| 98 | +export const writeLocalWorkspaceState = (input: { | |
| 80 | 99 | context: ResolvedLocalWorkspaceContext; |
| 81 | 100 | state: LocalWorkspaceState; |
| 82 | −}): Promise<void> => { | |
| 83 | − await fs.mkdir(input.context.stateDirectory, { recursive: true }); | |
| 84 | − await fs.writeFile( | |
| 85 | − localWorkspaceStatePath(input.context), | |
| 86 | − `${JSON.stringify(input.state, null, 2)}\n`, | |
| 87 | − "utf8", | |
| 88 | − ); | |
| 89 | −}; | |
| 101 | +}): Effect.Effect<void, Error> => | |
| 102 | + provideNodeFileSystem(Effect.gen(function* () { | |
| 103 | + const fs = yield* FileSystem.FileSystem; | |
| 104 | + yield* fs.makeDirectory(input.context.stateDirectory, { recursive: true }).pipe( | |
| 105 | + Effect.mapError(mapFileSystemError(input.context.stateDirectory, "create state directory")), | |
| 106 | + ); | |
| 107 | + yield* fs.writeFileString( | |
| 108 | + localWorkspaceStatePath(input.context), | |
| 109 | + `${JSON.stringify(input.state, null, 2)}\n`, | |
| 110 | + ).pipe( | |
| 111 | + Effect.mapError(mapFileSystemError(localWorkspaceStatePath(input.context), "write workspace state")), | |
| 112 | + ); | |
| 113 | + })); | |
| @@ -46,11 +46,7 @@ const pruneLocalWorkspaceState = (input: { · 45 unmodified lines | ||
| 46 | 46 | loadedConfig: LoadedLocalExecutorConfig; |
| 47 | 47 | }): Effect.Effect<LocalWorkspaceState, Error, never> => |
| 48 | 48 | Effect.gen(function* () { |
| 49 | − const currentState = yield* Effect.tryPromise({ | |
| 50 | − try: () => loadLocalWorkspaceState(input.context), | |
| 51 | − catch: (cause) => | |
| 52 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 53 | − }); | |
| 49 | + const currentState = yield* loadLocalWorkspaceState(input.context); | |
| 54 | 50 | |
| 55 | 51 | const configuredSourceIds = new Set( |
| 56 | 52 | Object.keys(input.loadedConfig.config?.sources ?? {}), |
| @@ -77,14 +73,9 @@ const pruneLocalWorkspaceState = (input: { · 20 unmodified lines | ||
| 77 | 73 | return currentState; |
| 78 | 74 | } |
| 79 | 75 | |
| 80 | − yield* Effect.tryPromise({ | |
| 81 | − try: () => | |
| 82 | − writeLocalWorkspaceState({ | |
| 83 | − context: input.context, | |
| 84 | − state: nextState, | |
| 85 | − }), | |
| 86 | − catch: (cause) => | |
| 87 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 76 | + yield* writeLocalWorkspaceState({ | |
| 77 | + context: input.context, | |
| 78 | + state: nextState, | |
| 88 | 79 | }); |
| 89 | 80 | |
| 90 | 81 | return nextState; |
| @@ -22,6 +22,7 @@ import { · 21 unmodified lines | ||
| 22 | 22 | } from "./local-runtime-context"; |
| 23 | 23 | import { |
| 24 | 24 | loadLocalWorkspaceState, |
| 25 | + type LocalWorkspaceState, | |
| 25 | 26 | writeLocalWorkspaceState, |
| 26 | 27 | } from "./local-workspace-state"; |
| 27 | 28 | import { |
| @@ -59,16 +60,8 @@ const localPolicyIdForConfigKey = (input: { · 31 unmodified lines | ||
| 59 | 60 | export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => |
| 60 | 61 | Effect.gen(function* () { |
| 61 | 62 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 62 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 63 | − try: () => loadLocalExecutorConfig(runtimeLocalWorkspace.context), | |
| 64 | − catch: (cause) => | |
| 65 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 66 | − }); | |
| 67 | − const workspaceState = yield* Effect.tryPromise({ | |
| 68 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 69 | − catch: (cause) => | |
| 70 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 71 | − }); | |
| 63 | + const loadedConfig = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 64 | + const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 72 | 65 | |
| 73 | 66 | const configEntries = Object.entries(loadedConfig.config?.policies ?? {}); |
| 74 | 67 | const policies = configEntries.map(([configKey, configPolicy]) => { |
| @@ -135,23 +128,18 @@ const writeLocalPolicyFiles = (input: { · 60 unmodified lines | ||
| 135 | 128 | operation: OperationErrors; |
| 136 | 129 | context: Parameters<typeof writeProjectLocalExecutorConfig>[0]["context"]; |
| 137 | 130 | projectConfig: LocalExecutorConfig; |
| 138 | − workspaceState: Awaited<ReturnType<typeof loadLocalWorkspaceState>>; | |
| 131 | + workspaceState: LocalWorkspaceState; | |
| 139 | 132 | }) => |
| 140 | − Effect.tryPromise({ | |
| 141 | − try: () => | |
| 142 | − Promise.all([ | |
| 143 | − writeProjectLocalExecutorConfig({ | |
| 144 | − context: input.context, | |
| 145 | − config: input.projectConfig, | |
| 146 | − }), | |
| 147 | − writeLocalWorkspaceState({ | |
| 148 | − context: input.context, | |
| 149 | − state: input.workspaceState, | |
| 150 | − }), | |
| 151 | − ]).then(() => undefined), | |
| 152 | − catch: (cause) => | |
| 153 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 154 | − }).pipe( | |
| 133 | + Effect.all([ | |
| 134 | + writeProjectLocalExecutorConfig({ | |
| 135 | + context: input.context, | |
| 136 | + config: input.projectConfig, | |
| 137 | + }), | |
| 138 | + writeLocalWorkspaceState({ | |
| 139 | + context: input.context, | |
| 140 | + state: input.workspaceState, | |
| 141 | + }), | |
| 142 | + ], { discard: true }).pipe( | |
| 155 | 143 | Effect.mapError((cause) => |
| 156 | 144 | input.operation.unknownStorage( |
| 157 | 145 | cause, |
| @@ -186,16 +186,8 @@ const makeRuntimeLocalWorkspaceState = async ( · 185 unmodified lines | ||
| 186 | 186 | const workspaceRoot = mkdtempSync( |
| 187 | 187 | join(tmpdir(), "executor-source-auth-service-"), |
| 188 | 188 | ); |
| 189 | − const context = yield* Effect.tryPromise({ | |
| 190 | − try: () => resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 191 | − catch: (cause) => | |
| 192 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 193 | − }); | |
| 194 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 195 | − try: () => loadLocalExecutorConfig(context), | |
| 196 | − catch: (cause) => | |
| 197 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 198 | − }); | |
| 189 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 190 | + const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 199 | 191 | |
| 200 | 192 | return { |
| 201 | 193 | context, |
| @@ -496,10 +488,10 @@ describe("source-auth-service", () => { · 294 unmodified lines | ||
| 496 | 488 | expect(Option.isNone(oldSecret)).toBe(true); |
| 497 | 489 | } |
| 498 | 490 | |
| 499 | − const localArtifact = await readLocalSourceArtifact({ | |
| 491 | + const localArtifact = await Effect.runPromise(readLocalSourceArtifact({ | |
| 500 | 492 | context: runtimeLocalWorkspace.context, |
| 501 | 493 | sourceId: connectedSource.id, |
| 502 | − }); | |
| 494 | + })); | |
| 503 | 495 | expect(localArtifact).not.toBeNull(); |
| 504 | 496 | expect(localArtifact?.operations.length).toBeGreaterThan(0); |
| 505 | 497 | expect( |
| @@ -59,16 +59,8 @@ const makeRuntimeLocalWorkspaceState = ( · 58 unmodified lines | ||
| 59 | 59 | const workspaceRoot = mkdtempSync( |
| 60 | 60 | join(tmpdir(), "executor-source-inspection-"), |
| 61 | 61 | ); |
| 62 | − const context = yield* Effect.tryPromise({ | |
| 63 | − try: () => resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 64 | − catch: (cause) => | |
| 65 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 66 | − }); | |
| 67 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 68 | − try: () => loadLocalExecutorConfig(context), | |
| 69 | − catch: (cause) => | |
| 70 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 71 | − }); | |
| 62 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 63 | + const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 72 | 64 | |
| 73 | 65 | return { |
| 74 | 66 | context, |
| @@ -215,15 +207,15 @@ describe("source-inspection", () => { · 140 unmodified lines | ||
| 215 | 207 | workspaceId, |
| 216 | 208 | sourceId, |
| 217 | 209 | }); |
| 218 | − await writeProjectLocalExecutorConfig({ | |
| 210 | + await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 219 | 211 | context: runtimeLocalWorkspace.context, |
| 220 | 212 | config: { |
| 221 | 213 | sources: { |
| 222 | 214 | [sourceId]: configSourceFromSource(source), |
| 223 | 215 | }, |
| 224 | 216 | }, |
| 225 | − }); | |
| 226 | − await writeLocalSourceArtifact({ | |
| 217 | + })); | |
| 218 | + await Effect.runPromise(writeLocalSourceArtifact({ | |
| 227 | 219 | context: runtimeLocalWorkspace.context, |
| 228 | 220 | sourceId, |
| 229 | 221 | artifact: buildLocalSourceArtifact({ |
| @@ -271,8 +263,8 @@ describe("source-inspection", () => { · 41 unmodified lines | ||
| 271 | 263 | })], |
| 272 | 264 | }, |
| 273 | 265 | }), |
| 274 | − }); | |
| 275 | − await writeLocalWorkspaceState({ | |
| 266 | + })); | |
| 267 | + await Effect.runPromise(writeLocalWorkspaceState({ | |
| 276 | 268 | context: runtimeLocalWorkspace.context, |
| 277 | 269 | state: { |
| 278 | 270 | version: 1, |
| @@ -287,7 +279,7 @@ describe("source-inspection", () => { · 8 unmodified lines | ||
| 287 | 279 | }, |
| 288 | 280 | policies: {}, |
| 289 | 281 | }, |
| 290 | − }); | |
| 282 | + })); | |
| 291 | 283 | |
| 292 | 284 | const inspection = await Effect.runPromise( |
| 293 | 285 | getSourceInspection({ |
| @@ -45,11 +45,7 @@ export const syncSourceMaterialization = (input: { · 44 unmodified lines | ||
| 45 | 45 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(input.source.workspaceId); |
| 46 | 46 | |
| 47 | 47 | if (!shouldIndexSource(input.source)) { |
| 48 | − const state = yield* Effect.tryPromise({ | |
| 49 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 50 | − catch: (cause) => | |
| 51 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 52 | − }); | |
| 48 | + const state = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 53 | 49 | const existingSourceState = state.sources[input.source.id]; |
| 54 | 50 | const nextState: LocalWorkspaceState = { |
| 55 | 51 | ...state, |
| @@ -64,14 +60,9 @@ export const syncSourceMaterialization = (input: { · 8 unmodified lines | ||
| 64 | 60 | }, |
| 65 | 61 | }, |
| 66 | 62 | }; |
| 67 | − yield* Effect.tryPromise({ | |
| 68 | − try: () => | |
| 69 | − writeLocalWorkspaceState({ | |
| 70 | − context: runtimeLocalWorkspace.context, | |
| 71 | − state: nextState, | |
| 72 | − }), | |
| 73 | − catch: (cause) => | |
| 74 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 63 | + yield* writeLocalWorkspaceState({ | |
| 64 | + context: runtimeLocalWorkspace.context, | |
| 65 | + state: nextState, | |
| 75 | 66 | }); |
| 76 | 67 | return; |
| 77 | 68 | } |
| @@ -89,25 +80,16 @@ export const syncSourceMaterialization = (input: { · 11 unmodified lines | ||
| 89 | 80 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 90 | 81 | }), |
| 91 | 82 | }); |
| 92 | − yield* Effect.tryPromise({ | |
| 93 | − try: () => | |
| 94 | − writeLocalSourceArtifact({ | |
| 95 | − context: runtimeLocalWorkspace.context, | |
| 96 | − sourceId: input.source.id, | |
| 97 | − artifact: buildLocalSourceArtifact({ | |
| 98 | − source: input.source, | |
| 99 | − materialization, | |
| 100 | − }), | |
| 101 | − }), | |
| 102 | − catch: (cause) => | |
| 103 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 83 | + yield* writeLocalSourceArtifact({ | |
| 84 | + context: runtimeLocalWorkspace.context, | |
| 85 | + sourceId: input.source.id, | |
| 86 | + artifact: buildLocalSourceArtifact({ | |
| 87 | + source: input.source, | |
| 88 | + materialization, | |
| 89 | + }), | |
| 104 | 90 | }); |
| 105 | 91 | |
| 106 | − const state = yield* Effect.tryPromise({ | |
| 107 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 108 | − catch: (cause) => | |
| 109 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 110 | − }); | |
| 92 | + const state = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 111 | 93 | const existingSourceState = state.sources[input.source.id]; |
| 112 | 94 | const nextState: LocalWorkspaceState = { |
| 113 | 95 | ...state, |
| @@ -122,14 +104,9 @@ export const syncSourceMaterialization = (input: { · 8 unmodified lines | ||
| 122 | 104 | }, |
| 123 | 105 | }, |
| 124 | 106 | }; |
| 125 | − yield* Effect.tryPromise({ | |
| 126 | − try: () => | |
| 127 | − writeLocalWorkspaceState({ | |
| 128 | − context: runtimeLocalWorkspace.context, | |
| 129 | − state: nextState, | |
| 130 | − }), | |
| 131 | − catch: (cause) => | |
| 132 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 107 | + yield* writeLocalWorkspaceState({ | |
| 108 | + context: runtimeLocalWorkspace.context, | |
| 109 | + state: nextState, | |
| 133 | 110 | }); |
| 134 | 111 | }); |
| 135 | 112 | |
| @@ -148,17 +125,12 @@ export const persistMcpRecipeMaterializationFromManifest = (input: { · 12 unmodified lines | ||
| 148 | 125 | manifestEntries: input.manifestEntries, |
| 149 | 126 | }); |
| 150 | 127 | |
| 151 | − yield* Effect.tryPromise({ | |
| 152 | − try: () => | |
| 153 | − writeLocalSourceArtifact({ | |
| 154 | − context: runtimeLocalWorkspace.context, | |
| 155 | − sourceId: input.source.id, | |
| 156 | − artifact: buildLocalSourceArtifact({ | |
| 157 | − source: input.source, | |
| 158 | − materialization, | |
| 159 | − }), | |
| 160 | − }), | |
| 161 | − catch: (cause) => | |
| 162 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 128 | + yield* writeLocalSourceArtifact({ | |
| 129 | + context: runtimeLocalWorkspace.context, | |
| 130 | + sourceId: input.source.id, | |
| 131 | + artifact: buildLocalSourceArtifact({ | |
| 132 | + source: input.source, | |
| 133 | + materialization, | |
| 134 | + }), | |
| 163 | 135 | }); |
| 164 | 136 | }); |
| @@ -66,16 +66,8 @@ const makeRuntimeLocalWorkspaceState = ( · 65 unmodified lines | ||
| 66 | 66 | const workspaceRoot = mkdtempSync( |
| 67 | 67 | join(tmpdir(), "executor-source-recipes-runtime-"), |
| 68 | 68 | ); |
| 69 | − const context = yield* Effect.tryPromise({ | |
| 70 | − try: () => resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 71 | − catch: (cause) => | |
| 72 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 73 | − }); | |
| 74 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 75 | − try: () => loadLocalExecutorConfig(context), | |
| 76 | − catch: (cause) => | |
| 77 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 78 | − }); | |
| 69 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 70 | + const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 79 | 71 | |
| 80 | 72 | return { |
| 81 | 73 | context, |
| @@ -362,18 +354,18 @@ const seedLocalWorkspaceSources = async (input: { · 280 unmodified lines | ||
| 362 | 354 | }; |
| 363 | 355 | }>; |
| 364 | 356 | }) => { |
| 365 | − await writeProjectLocalExecutorConfig({ | |
| 357 | + await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 366 | 358 | context: input.runtimeLocalWorkspace.context, |
| 367 | 359 | config: { |
| 368 | 360 | sources: Object.fromEntries( |
| 369 | 361 | input.fixtures.map(({ source }) => [source.id, configSourceFromSource(source)]), |
| 370 | 362 | ), |
| 371 | 363 | }, |
| 372 | − }); | |
| 364 | + })); | |
| 373 | 365 | |
| 374 | 366 | await Promise.all( |
| 375 | 367 | input.fixtures.map(({ source, materialization }) => |
| 376 | − writeLocalSourceArtifact({ | |
| 368 | + Effect.runPromise(writeLocalSourceArtifact({ | |
| 377 | 369 | context: input.runtimeLocalWorkspace.context, |
| 378 | 370 | sourceId: source.id, |
| 379 | 371 | artifact: buildLocalSourceArtifact({ |
| @@ -381,9 +373,10 @@ const seedLocalWorkspaceSources = async (input: { · 1 unmodified line | ||
| 381 | 373 | materialization, |
| 382 | 374 | }), |
| 383 | 375 | })), |
| 376 | + ), | |
| 384 | 377 | ); |
| 385 | 378 | |
| 386 | − await writeLocalWorkspaceState({ | |
| 379 | + await Effect.runPromise(writeLocalWorkspaceState({ | |
| 387 | 380 | context: input.runtimeLocalWorkspace.context, |
| 388 | 381 | state: { |
| 389 | 382 | version: 1, |
| @@ -401,7 +394,7 @@ const seedLocalWorkspaceSources = async (input: { · 11 unmodified lines | ||
| 401 | 394 | ), |
| 402 | 395 | policies: {}, |
| 403 | 396 | }, |
| 404 | − }); | |
| 397 | + })); | |
| 405 | 398 | }; |
| 406 | 399 | |
| 407 | 400 | describe("source-recipes-runtime", () => { |
| @@ -774,7 +767,7 @@ describe("source-recipes-runtime", () => { · 366 unmodified lines | ||
| 774 | 767 | const runtimeLocalWorkspace = |
| 775 | 768 | await makeRuntimeLocalWorkspaceState(workspaceId); |
| 776 | 769 | |
| 777 | − await writeProjectLocalExecutorConfig({ | |
| 770 | + await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 778 | 771 | context: runtimeLocalWorkspace.context, |
| 779 | 772 | config: { |
| 780 | 773 | sources: { |
| @@ -787,8 +780,8 @@ describe("source-recipes-runtime", () => { · 6 unmodified lines | ||
| 787 | 780 | ), |
| 788 | 781 | }, |
| 789 | 782 | }, |
| 790 | − }); | |
| 791 | − await writeLocalWorkspaceState({ | |
| 783 | + })); | |
| 784 | + await Effect.runPromise(writeLocalWorkspaceState({ | |
| 792 | 785 | context: runtimeLocalWorkspace.context, |
| 793 | 786 | state: { |
| 794 | 787 | version: 1, |
| @@ -803,7 +796,7 @@ describe("source-recipes-runtime", () => { · 8 unmodified lines | ||
| 803 | 796 | }, |
| 804 | 797 | policies: {}, |
| 805 | 798 | }, |
| 806 | − }); | |
| 799 | + })); | |
| 807 | 800 | |
| 808 | 801 | await expect( |
| 809 | 802 | Effect.runPromise( |
| @@ -834,7 +827,7 @@ describe("source-recipes-runtime", () => { · 24 unmodified lines | ||
| 834 | 827 | operations: [], |
| 835 | 828 | }, |
| 836 | 829 | }); |
| 837 | − await writeLocalSourceArtifact({ | |
| 830 | + await Effect.runPromise(writeLocalSourceArtifact({ | |
| 838 | 831 | context: runtimeLocalWorkspace.context, |
| 839 | 832 | sourceId, |
| 840 | 833 | artifact: { |
| @@ -844,7 +837,7 @@ describe("source-recipes-runtime", () => { · 3 unmodified lines | ||
| 844 | 837 | manifestJson: "{bad-json", |
| 845 | 838 | }, |
| 846 | 839 | }, |
| 847 | − }); | |
| 840 | + })); | |
| 848 | 841 | |
| 849 | 842 | await expect( |
| 850 | 843 | Effect.runPromise( |
| @@ -167,14 +167,9 @@ export const loadWorkspaceSourceRecipes = (input: { · 166 unmodified lines | ||
| 167 | 167 | }); |
| 168 | 168 | const localRecipes = yield* Effect.forEach(sources, (source) => |
| 169 | 169 | Effect.gen(function* () { |
| 170 | − const artifact = yield* Effect.tryPromise({ | |
| 171 | − try: () => | |
| 172 | − readLocalSourceArtifact({ | |
| 173 | − context: runtimeLocalWorkspace.context, | |
| 174 | − sourceId: source.id, | |
| 175 | − }), | |
| 176 | − catch: (cause) => | |
| 177 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 170 | + const artifact = yield* readLocalSourceArtifact({ | |
| 171 | + context: runtimeLocalWorkspace.context, | |
| 172 | + sourceId: source.id, | |
| 178 | 173 | }); |
| 179 | 174 | if (artifact === null) { |
| 180 | 175 | return null; |
| @@ -231,14 +226,9 @@ export const loadSourceWithRecipe = (input: { · 50 unmodified lines | ||
| 231 | 226 | sourceId: input.sourceId, |
| 232 | 227 | actorAccountId: input.actorAccountId, |
| 233 | 228 | }); |
| 234 | − const artifact = yield* Effect.tryPromise({ | |
| 235 | − try: () => | |
| 236 | − readLocalSourceArtifact({ | |
| 237 | − context: runtimeLocalWorkspace.context, | |
| 238 | − sourceId: source.id, | |
| 239 | − }), | |
| 240 | − catch: (cause) => | |
| 241 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 229 | + const artifact = yield* readLocalSourceArtifact({ | |
| 230 | + context: runtimeLocalWorkspace.context, | |
| 231 | + sourceId: source.id, | |
| 242 | 232 | }); |
| 243 | 233 | if (artifact === null) { |
| 244 | 234 | return yield* Effect.fail( |
| @@ -56,16 +56,8 @@ const makeRuntimeLocalWorkspace = (input: { · 55 unmodified lines | ||
| 56 | 56 | }) => |
| 57 | 57 | Effect.gen(function* () { |
| 58 | 58 | const workspaceRoot = mkdtempSync(join(tmpdir(), "executor-source-store-")); |
| 59 | − const context = yield* Effect.tryPromise({ | |
| 60 | − try: () => resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 61 | − catch: (cause) => | |
| 62 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 63 | − }); | |
| 64 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 65 | − try: () => loadLocalExecutorConfig(context), | |
| 66 | − catch: (cause) => | |
| 67 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 68 | − }); | |
| 59 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 60 | + const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 69 | 61 | |
| 70 | 62 | return { |
| 71 | 63 | context, |
| @@ -245,17 +237,9 @@ describe("source-store", () => { · 173 unmodified lines | ||
| 245 | 237 | expect(Option.isNone(yield* persistence.rows.secretMaterials.getById(secondTokenId))).toBe(true); |
| 246 | 238 | expect(yield* persistence.rows.authArtifacts.listByWorkspaceId(workspaceId)).toHaveLength(0); |
| 247 | 239 | expect(yield* persistence.rows.sourceAuthSessions.listByWorkspaceId(workspaceId)).toHaveLength(0); |
| 248 | − const config = yield* Effect.tryPromise({ | |
| 249 | − try: () => loadLocalExecutorConfig(runtimeLocalWorkspace.context), | |
| 250 | − catch: (cause) => | |
| 251 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 252 | − }); | |
| 240 | + const config = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 253 | 241 | expect(config.config?.sources?.[sourceId]).toBeUndefined(); |
| 254 | − const workspaceState = yield* Effect.tryPromise({ | |
| 255 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 256 | − catch: (cause) => | |
| 257 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 258 | − }); | |
| 242 | + const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 259 | 243 | expect(workspaceState.sources[sourceId]).toBeUndefined(); |
| 260 | 244 | }), |
| 261 | 245 | ); |
| @@ -234,16 +234,8 @@ const configAuthFromSource = (input: { · 233 unmodified lines | ||
| 234 | 234 | const resolveRuntimeLocalWorkspace = (workspaceId: WorkspaceId) => |
| 235 | 235 | Effect.gen(function* () { |
| 236 | 236 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 237 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 238 | − try: () => loadLocalExecutorConfig(runtimeLocalWorkspace.context), | |
| 239 | − catch: (cause) => | |
| 240 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 241 | − }); | |
| 242 | − const workspaceState = yield* Effect.tryPromise({ | |
| 243 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 244 | − catch: (cause) => | |
| 245 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 246 | − }); | |
| 237 | + const loadedConfig = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 238 | + const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 247 | 239 | |
| 248 | 240 | return { |
| 249 | 241 | context: runtimeLocalWorkspace.context, |
| @@ -297,13 +289,9 @@ const buildLocalSourceRecord = (input: { · 47 unmodified lines | ||
| 297 | 289 | updatedAt: existingState?.updatedAt ?? Date.now(), |
| 298 | 290 | }); |
| 299 | 291 | |
| 300 | − const artifact = yield* Effect.tryPromise({ | |
| 301 | − try: () => readLocalSourceArtifact({ | |
| 302 | − context: input.context, | |
| 303 | − sourceId: input.sourceId, | |
| 304 | − }), | |
| 305 | − catch: (cause) => | |
| 306 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 292 | + const artifact = yield* readLocalSourceArtifact({ | |
| 293 | + context: input.context, | |
| 294 | + sourceId: input.sourceId, | |
| 307 | 295 | }).pipe( |
| 308 | 296 | Effect.catchAll(() => Effect.succeed(null)), |
| 309 | 297 | ); |
| @@ -524,17 +512,12 @@ export const removeSourceById = (rows: SqlControlPlaneRows, input: { · 214 unmodified lines | ||
| 524 | 512 | ...(projectConfig.sources ?? {}), |
| 525 | 513 | }; |
| 526 | 514 | delete sources[input.sourceId]; |
| 527 | − yield* Effect.tryPromise({ | |
| 528 | − try: () => | |
| 529 | − writeProjectLocalExecutorConfig({ | |
| 530 | − context: localWorkspace.context, | |
| 531 | − config: { | |
| 532 | − ...projectConfig, | |
| 533 | − sources, | |
| 534 | − }, | |
| 535 | − }), | |
| 536 | − catch: (cause) => | |
| 537 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 515 | + yield* writeProjectLocalExecutorConfig({ | |
| 516 | + context: localWorkspace.context, | |
| 517 | + config: { | |
| 518 | + ...projectConfig, | |
| 519 | + sources, | |
| 520 | + }, | |
| 538 | 521 | }); |
| 539 | 522 | |
| 540 | 523 | const { |
| @@ -545,23 +528,13 @@ export const removeSourceById = (rows: SqlControlPlaneRows, input: { · 4 unmodified lines | ||
| 545 | 528 | ...localWorkspace.workspaceState, |
| 546 | 529 | sources: remainingSources, |
| 547 | 530 | }; |
| 548 | − yield* Effect.tryPromise({ | |
| 549 | − try: () => | |
| 550 | − writeLocalWorkspaceState({ | |
| 551 | − context: localWorkspace.context, | |
| 552 | − state: workspaceState, | |
| 553 | − }), | |
| 554 | − catch: (cause) => | |
| 555 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 531 | + yield* writeLocalWorkspaceState({ | |
| 532 | + context: localWorkspace.context, | |
| 533 | + state: workspaceState, | |
| 556 | 534 | }); |
| 557 | − yield* Effect.tryPromise({ | |
| 558 | − try: () => | |
| 559 | − removeLocalSourceArtifact({ | |
| 560 | − context: localWorkspace.context, | |
| 561 | − sourceId: input.sourceId, | |
| 562 | − }), | |
| 563 | − catch: (cause) => | |
| 564 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 535 | + yield* removeLocalSourceArtifact({ | |
| 536 | + context: localWorkspace.context, | |
| 537 | + sourceId: input.sourceId, | |
| 565 | 538 | }); |
| 566 | 539 | |
| 567 | 540 | yield* rows.sourceAuthSessions.removeByWorkspaceAndSourceId( |
| @@ -621,17 +594,12 @@ export const persistSource = ( · 53 unmodified lines | ||
| 621 | 594 | existingConfigAuth: existingConfigSource?.connection.auth, |
| 622 | 595 | config: localWorkspace.loadedConfig.config, |
| 623 | 596 | }); |
| 624 | − yield* Effect.tryPromise({ | |
| 625 | − try: () => | |
| 626 | − writeProjectLocalExecutorConfig({ | |
| 627 | − context: localWorkspace.context, | |
| 628 | − config: { | |
| 629 | − ...projectConfig, | |
| 630 | − sources, | |
| 631 | − }, | |
| 632 | − }), | |
| 633 | − catch: (cause) => | |
| 634 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 597 | + yield* writeProjectLocalExecutorConfig({ | |
| 598 | + context: localWorkspace.context, | |
| 599 | + config: { | |
| 600 | + ...projectConfig, | |
| 601 | + sources, | |
| 602 | + }, | |
| 635 | 603 | }); |
| 636 | 604 | |
| 637 | 605 | const { runtimeAuthArtifact, importAuthArtifact } = splitSourceForStorage({ |
| @@ -715,14 +683,9 @@ export const persistSource = ( · 77 unmodified lines | ||
| 715 | 683 | }, |
| 716 | 684 | }, |
| 717 | 685 | }; |
| 718 | − yield* Effect.tryPromise({ | |
| 719 | − try: () => | |
| 720 | − writeLocalWorkspaceState({ | |
| 721 | − context: localWorkspace.context, | |
| 722 | − state: workspaceState, | |
| 723 | − }), | |
| 724 | − catch: (cause) => | |
| 725 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 686 | + yield* writeLocalWorkspaceState({ | |
| 687 | + context: localWorkspace.context, | |
| 688 | + state: workspaceState, | |
| 726 | 689 | }); |
| 727 | 690 | |
| 728 | 691 | return yield* loadSourceById(rows, { |
| @@ -631,80 +631,65 @@ const persistConnectedEchoTool = (input: { · 630 unmodified lines | ||
| 631 | 631 | updatedAt: now, |
| 632 | 632 | }; |
| 633 | 633 | |
| 634 | − yield* Effect.tryPromise({ | |
| 635 | − try: () => | |
| 636 | − writeProjectLocalExecutorConfig({ | |
| 637 | − context: input.runtimeLocalWorkspace.context, | |
| 638 | − config: { | |
| 639 | − sources: { | |
| 640 | − [input.sourceId]: { | |
| 641 | − kind: "mcp", | |
| 642 | − name: source.name, | |
| 643 | − ...(source.namespace ? { namespace: source.namespace } : {}), | |
| 644 | − connection: { | |
| 645 | − endpoint: source.endpoint, | |
| 646 | − }, | |
| 647 | − binding: { | |
| 648 | − transport: "streamable-http", | |
| 649 | − }, | |
| 650 | − }, | |
| 634 | + yield* writeProjectLocalExecutorConfig({ | |
| 635 | + context: input.runtimeLocalWorkspace.context, | |
| 636 | + config: { | |
| 637 | + sources: { | |
| 638 | + [input.sourceId]: { | |
| 639 | + kind: "mcp", | |
| 640 | + name: source.name, | |
| 641 | + ...(source.namespace ? { namespace: source.namespace } : {}), | |
| 642 | + connection: { | |
| 643 | + endpoint: source.endpoint, | |
| 644 | + }, | |
| 645 | + binding: { | |
| 646 | + transport: "streamable-http", | |
| 651 | 647 | }, |
| 652 | 648 | }, |
| 653 | − }), | |
| 654 | − catch: (cause) => | |
| 655 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 649 | + }, | |
| 650 | + }, | |
| 656 | 651 | }); |
| 657 | − yield* Effect.tryPromise({ | |
| 658 | − try: () => | |
| 659 | − writeLocalSourceArtifact({ | |
| 660 | − context: input.runtimeLocalWorkspace.context, | |
| 661 | − sourceId: source.id, | |
| 662 | − artifact: buildLocalSourceArtifact({ | |
| 663 | − source, | |
| 664 | − materialization: materializationFromMcpManifestEntries({ | |
| 665 | − recipeRevisionId: "src_recipe_rev_materialization" as never, | |
| 666 | − endpoint: source.endpoint, | |
| 667 | − manifestEntries: [ | |
| 668 | − { | |
| 669 | − toolId: "echo", | |
| 670 | − toolName: "echo", | |
| 671 | − description: "Echo the provided string", | |
| 672 | − inputSchemaJson: JSON.stringify({ | |
| 673 | − type: "object", | |
| 674 | − properties: { | |
| 675 | − value: { type: "string" }, | |
| 676 | − }, | |
| 677 | − required: ["value"], | |
| 678 | − additionalProperties: false, | |
| 679 | − }), | |
| 652 | + yield* writeLocalSourceArtifact({ | |
| 653 | + context: input.runtimeLocalWorkspace.context, | |
| 654 | + sourceId: source.id, | |
| 655 | + artifact: buildLocalSourceArtifact({ | |
| 656 | + source, | |
| 657 | + materialization: materializationFromMcpManifestEntries({ | |
| 658 | + recipeRevisionId: "src_recipe_rev_materialization" as never, | |
| 659 | + endpoint: source.endpoint, | |
| 660 | + manifestEntries: [ | |
| 661 | + { | |
| 662 | + toolId: "echo", | |
| 663 | + toolName: "echo", | |
| 664 | + description: "Echo the provided string", | |
| 665 | + inputSchemaJson: JSON.stringify({ | |
| 666 | + type: "object", | |
| 667 | + properties: { | |
| 668 | + value: { type: "string" }, | |
| 680 | 669 | }, |
| 681 | − ], | |
| 682 | − }), | |
| 683 | − }), | |
| 670 | + required: ["value"], | |
| 671 | + additionalProperties: false, | |
| 672 | + }), | |
| 673 | + }, | |
| 674 | + ], | |
| 684 | 675 | }), |
| 685 | − catch: (cause) => | |
| 686 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 676 | + }), | |
| 687 | 677 | }); |
| 688 | − yield* Effect.tryPromise({ | |
| 689 | − try: () => | |
| 690 | − writeLocalWorkspaceState({ | |
| 691 | − context: input.runtimeLocalWorkspace.context, | |
| 692 | − state: { | |
| 693 | − version: 1, | |
| 694 | − sources: { | |
| 695 | − [input.sourceId]: { | |
| 696 | − status: "connected", | |
| 697 | − lastError: null, | |
| 698 | − sourceHash: null, | |
| 699 | − createdAt: now, | |
| 700 | − updatedAt: now, | |
| 701 | − }, | |
| 702 | − }, | |
| 703 | − policies: {}, | |
| 678 | + yield* writeLocalWorkspaceState({ | |
| 679 | + context: input.runtimeLocalWorkspace.context, | |
| 680 | + state: { | |
| 681 | + version: 1, | |
| 682 | + sources: { | |
| 683 | + [input.sourceId]: { | |
| 684 | + status: "connected", | |
| 685 | + lastError: null, | |
| 686 | + sourceHash: null, | |
| 687 | + createdAt: now, | |
| 688 | + updatedAt: now, | |
| 704 | 689 | }, |
| 705 | − }), | |
| 706 | − catch: (cause) => | |
| 707 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 690 | + }, | |
| 691 | + policies: {}, | |
| 692 | + }, | |
| 708 | 693 | }); |
| 709 | 694 | }) as Effect.Effect<void, Error, never>; |
| 710 | 695 | |
| @@ -931,38 +916,31 @@ const persistConnectedGithubOpenApiSource = (input: { · 220 unmodified lines | ||
| 931 | 916 | ), |
| 932 | 917 | ); |
| 933 | 918 | const definitions = compileOpenApiToolDefinitions(manifest); |
| 934 | − yield* Effect.tryPromise({ | |
| 935 | − try: () => | |
| 936 | − writeProjectLocalExecutorConfig({ | |
| 937 | − context: input.runtimeLocalWorkspace.context, | |
| 938 | − config: { | |
| 939 | − sources: { | |
| 940 | − [input.sourceId]: { | |
| 941 | − kind: "openapi", | |
| 942 | − name: source.name, | |
| 943 | − ...(source.namespace ? { namespace: source.namespace } : {}), | |
| 944 | − connection: { | |
| 945 | − endpoint: source.endpoint, | |
| 946 | − ...(configAuth !== undefined ? { auth: configAuth } : {}), | |
| 947 | − }, | |
| 948 | − binding: { | |
| 949 | − specUrl: String(source.binding.specUrl), | |
| 950 | − defaultHeaders: null, | |
| 951 | − }, | |
| 952 | − }, | |
| 919 | + yield* writeProjectLocalExecutorConfig({ | |
| 920 | + context: input.runtimeLocalWorkspace.context, | |
| 921 | + config: { | |
| 922 | + sources: { | |
| 923 | + [input.sourceId]: { | |
| 924 | + kind: "openapi", | |
| 925 | + name: source.name, | |
| 926 | + ...(source.namespace ? { namespace: source.namespace } : {}), | |
| 927 | + connection: { | |
| 928 | + endpoint: source.endpoint, | |
| 929 | + ...(configAuth !== undefined ? { auth: configAuth } : {}), | |
| 930 | + }, | |
| 931 | + binding: { | |
| 932 | + specUrl: String(source.binding.specUrl), | |
| 933 | + defaultHeaders: null, | |
| 953 | 934 | }, |
| 954 | 935 | }, |
| 955 | − }), | |
| 956 | − catch: (cause) => | |
| 957 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 936 | + }, | |
| 937 | + }, | |
| 958 | 938 | }); |
| 959 | 939 | const refEntries = Object.entries(manifest.refHintTable ?? {}); |
| 960 | − yield* Effect.tryPromise({ | |
| 961 | − try: () => | |
| 962 | − writeLocalSourceArtifact({ | |
| 963 | − context: input.runtimeLocalWorkspace.context, | |
| 964 | − sourceId: source.id, | |
| 965 | − artifact: buildLocalSourceArtifact({ | |
| 940 | + yield* writeLocalSourceArtifact({ | |
| 941 | + context: input.runtimeLocalWorkspace.context, | |
| 942 | + sourceId: source.id, | |
| 943 | + artifact: buildLocalSourceArtifact({ | |
| 966 | 944 | source: { |
| 967 | 945 | ...source, |
| 968 | 946 | sourceHash: manifest.sourceHash, |
| @@ -1055,30 +1033,22 @@ const persistConnectedGithubOpenApiSource = (input: { · 86 unmodified lines | ||
| 1055 | 1033 | }), |
| 1056 | 1034 | }, |
| 1057 | 1035 | }), |
| 1058 | − }), | |
| 1059 | − catch: (cause) => | |
| 1060 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1061 | 1036 | }); |
| 1062 | − yield* Effect.tryPromise({ | |
| 1063 | − try: () => | |
| 1064 | − writeLocalWorkspaceState({ | |
| 1065 | − context: input.runtimeLocalWorkspace.context, | |
| 1066 | − state: { | |
| 1067 | − version: 1, | |
| 1068 | − sources: { | |
| 1069 | − [input.sourceId]: { | |
| 1070 | − status: "connected", | |
| 1071 | − lastError: null, | |
| 1072 | − sourceHash: manifest.sourceHash, | |
| 1073 | − createdAt: now, | |
| 1074 | − updatedAt: now, | |
| 1075 | − }, | |
| 1076 | − }, | |
| 1077 | − policies: {}, | |
| 1037 | + yield* writeLocalWorkspaceState({ | |
| 1038 | + context: input.runtimeLocalWorkspace.context, | |
| 1039 | + state: { | |
| 1040 | + version: 1, | |
| 1041 | + sources: { | |
| 1042 | + [input.sourceId]: { | |
| 1043 | + status: "connected", | |
| 1044 | + lastError: null, | |
| 1045 | + sourceHash: manifest.sourceHash, | |
| 1046 | + createdAt: now, | |
| 1047 | + updatedAt: now, | |
| 1078 | 1048 | }, |
| 1079 | − }), | |
| 1080 | − catch: (cause) => | |
| 1081 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1049 | + }, | |
| 1050 | + policies: {}, | |
| 1051 | + }, | |
| 1082 | 1052 | }); |
| 1083 | 1053 | |
| 1084 | 1054 | return; |
| @@ -1087,16 +1057,8 @@ const persistConnectedGithubOpenApiSource = (input: { · 2 unmodified lines | ||
| 1087 | 1057 | const makeRuntimeLocalWorkspaceState = (workspaceId: WorkspaceId): Effect.Effect<RuntimeLocalWorkspaceState, Error, never> => |
| 1088 | 1058 | Effect.gen(function* () { |
| 1089 | 1059 | const workspaceRoot = mkdtempSync(join(tmpdir(), "executor-workspace-env-")); |
| 1090 | − const context = yield* Effect.tryPromise({ | |
| 1091 | − try: () => resolveLocalWorkspaceContext({ workspaceRoot }), | |
| 1092 | − catch: (cause) => | |
| 1093 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1094 | − }); | |
| 1095 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 1096 | − try: () => loadLocalExecutorConfig(context), | |
| 1097 | − catch: (cause) => | |
| 1098 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1099 | − }); | |
| 1060 | + const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 1061 | + const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 1100 | 1062 | return { |
| 1101 | 1063 | context, |
| 1102 | 1064 | installation: { |
| @@ -1377,34 +1339,21 @@ describe("workspace-execution-environment", () => { · 274 unmodified lines | ||
| 1377 | 1339 | encodeURIComponent("exec_add_openapi:executor.sources.add:"), |
| 1378 | 1340 | ); |
| 1379 | 1341 | |
| 1380 | − const loadedConfig = yield* Effect.tryPromise({ | |
| 1381 | − try: () => loadLocalExecutorConfig(runtimeLocalWorkspace.context), | |
| 1382 | − catch: (cause) => | |
| 1383 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1384 | − }); | |
| 1342 | + const loadedConfig = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 1385 | 1343 | expect(loadedConfig.config?.sources?.[added.id]).toMatchObject({ |
| 1386 | 1344 | kind: "openapi", |
| 1387 | 1345 | connection: { |
| 1388 | 1346 | endpoint: `${specServer.baseUrl}/`, |
| 1389 | 1347 | }, |
| 1390 | 1348 | }); |
| 1391 | − const artifact = yield* Effect.tryPromise({ | |
| 1392 | − try: () => | |
| 1393 | − readLocalSourceArtifact({ | |
| 1394 | − context: runtimeLocalWorkspace.context, | |
| 1395 | − sourceId: added.id, | |
| 1396 | − }), | |
| 1397 | − catch: (cause) => | |
| 1398 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1349 | + const artifact = yield* readLocalSourceArtifact({ | |
| 1350 | + context: runtimeLocalWorkspace.context, | |
| 1351 | + sourceId: added.id, | |
| 1399 | 1352 | }); |
| 1400 | 1353 | expect(artifact?.documents[0]?.contentText).toContain( |
| 1401 | 1354 | '"operationId":"repos.getRepo"', |
| 1402 | 1355 | ); |
| 1403 | − const workspaceState = yield* Effect.tryPromise({ | |
| 1404 | − try: () => loadLocalWorkspaceState(runtimeLocalWorkspace.context), | |
| 1405 | − catch: (cause) => | |
| 1406 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1407 | − }); | |
| 1356 | + const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 1408 | 1357 | expect(workspaceState.sources[added.id]?.status).toBe("connected"); |
| 1409 | 1358 | |
| 1410 | 1359 | const freshEnvironment = yield* resolveEnvironment({ |
| @@ -1511,14 +1460,9 @@ describe("workspace-execution-environment", () => { · 100 unmodified lines | ||
| 1511 | 1460 | expect(added.auth.kind).toBe("bearer"); |
| 1512 | 1461 | expect(added.auth.token?.providerId).toBe("postgres"); |
| 1513 | 1462 | |
| 1514 | − const artifact = yield* Effect.tryPromise({ | |
| 1515 | − try: () => | |
| 1516 | − readLocalSourceArtifact({ | |
| 1517 | − context: runtimeLocalWorkspace.context, | |
| 1518 | − sourceId: added.id, | |
| 1519 | − }), | |
| 1520 | − catch: (cause) => | |
| 1521 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1463 | + const artifact = yield* readLocalSourceArtifact({ | |
| 1464 | + context: runtimeLocalWorkspace.context, | |
| 1465 | + sourceId: added.id, | |
| 1522 | 1466 | }); |
| 1523 | 1467 | expect(artifact?.documents[0]?.contentText).toContain('"__schema"'); |
| 1524 | 1468 | expect(artifact?.schemaBundles.length).toBe(1); |
| @@ -1526,22 +1470,17 @@ describe("workspace-execution-environment", () => { · 1 unmodified line | ||
| 1526 | 1470 | throw new Error(`Missing local source artifact for ${added.id}`); |
| 1527 | 1471 | } |
| 1528 | 1472 | |
| 1529 | − yield* Effect.tryPromise({ | |
| 1530 | − try: () => | |
| 1531 | − writeLocalSourceArtifact({ | |
| 1532 | − context: runtimeLocalWorkspace.context, | |
| 1533 | − sourceId: added.id, | |
| 1534 | − artifact: { | |
| 1535 | − ...artifact, | |
| 1536 | − revision: { | |
| 1537 | − ...artifact.revision, | |
| 1538 | − manifestJson: null, | |
| 1539 | − updatedAt: Date.now(), | |
| 1540 | − }, | |
| 1541 | − }, | |
| 1542 | − }), | |
| 1543 | − catch: (cause) => | |
| 1544 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 1473 | + yield* writeLocalSourceArtifact({ | |
| 1474 | + context: runtimeLocalWorkspace.context, | |
| 1475 | + sourceId: added.id, | |
| 1476 | + artifact: { | |
| 1477 | + ...artifact, | |
| 1478 | + revision: { | |
| 1479 | + ...artifact.revision, | |
| 1480 | + manifestJson: null, | |
| 1481 | + updatedAt: Date.now(), | |
| 1482 | + }, | |
| 1483 | + }, | |
| 1545 | 1484 | }); |
| 1546 | 1485 | |
| 1547 | 1486 | const freshEnvironment = yield* resolveEnvironment({ |