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