Unified · Split
| @@ -22,14 +22,16 @@ import { · 21 unmodified lines | |||
| 22 | controlPlaneOpenApiSpec, | 22 | controlPlaneOpenApiSpec, |
| 23 | } from "@executor/platform-api"; | 23 | } from "@executor/platform-api"; |
| 24 | import { | 24 | import { |
| 25 | buildLocalSourceArtifact, | ||
| 26 | catalogSyncResultFromMcpManifest, | 25 | catalogSyncResultFromMcpManifest, |
| 27 | deriveLocalInstallation, | 26 | type ResolveExecutionEnvironment, |
| 27 | } from "@executor/platform-sdk/runtime"; | ||
| 28 | import { | ||
| 29 | buildLocalSourceArtifact, | ||
| 30 | getOrProvisionLocalInstallation, | ||
| 28 | resolveLocalWorkspaceContext, | 31 | resolveLocalWorkspaceContext, |
| 29 | writeLocalSourceArtifact, | 32 | writeLocalSourceArtifact, |
| 30 | writeProjectLocalExecutorConfig, | 33 | writeProjectLocalExecutorConfig, |
| 31 | type ResolveExecutionEnvironment, | 34 | } from "@executor/platform-sdk-file"; |
| 32 | } from "@executor/platform-sdk/runtime"; | ||
| 33 | import { SourceIdSchema } from "@executor/platform-sdk/schema"; | 35 | import { SourceIdSchema } from "@executor/platform-sdk/schema"; |
| 34 | import { makeSesExecutor } from "@executor/runtime-ses"; | 36 | import { makeSesExecutor } from "@executor/runtime-ses"; |
| 35 | import { Client } from "@modelcontextprotocol/sdk/client/index.js"; | 37 | import { Client } from "@modelcontextprotocol/sdk/client/index.js"; |
| @@ -97,7 +99,7 @@ const writeConfiguredLocalMcpSource = (input: { · 61 unmodified lines | |||
| 97 | const context = yield* resolveLocalWorkspaceContext({ | 99 | const context = yield* resolveLocalWorkspaceContext({ |
| 98 | workspaceRoot: input.workspaceRoot, | 100 | workspaceRoot: input.workspaceRoot, |
| 99 | }); | 101 | }); |
| 100 | const installation = deriveLocalInstallation(context); | 102 | const installation = yield* getOrProvisionLocalInstallation({ context }); |
| 101 | 103 | ||
| 102 | yield* writeProjectLocalExecutorConfig({ | 104 | yield* writeProjectLocalExecutorConfig({ |
| 103 | context, | 105 | context, |
| @@ -30,6 +30,7 @@ · 29 unmodified lines | |||
| 30 | "@executor/platform-api": "workspace:*", | 30 | "@executor/platform-api": "workspace:*", |
| 31 | "@executor/platform-internal": "workspace:*", | 31 | "@executor/platform-internal": "workspace:*", |
| 32 | "@executor/platform-sdk": "workspace:*", | 32 | "@executor/platform-sdk": "workspace:*", |
| 33 | "@executor/platform-sdk-file": "workspace:*", | ||
| 33 | "@executor/server": "workspace:*", | 34 | "@executor/server": "workspace:*", |
| 34 | "effect": "catalog:", | 35 | "effect": "catalog:", |
| 35 | }, | 36 | }, |
| @@ -136,6 +137,7 @@ · 100 unmodified lines | |||
| 136 | "@effect/platform": "catalog:", | 137 | "@effect/platform": "catalog:", |
| 137 | "@effect/platform-node": "catalog:", | 138 | "@effect/platform-node": "catalog:", |
| 138 | "@effect/vitest": "catalog:", | 139 | "@effect/vitest": "catalog:", |
| 140 | "@executor/platform-sdk-file": "workspace:*", | ||
| 139 | "@types/jsdom": "^21.1.7", | 141 | "@types/jsdom": "^21.1.7", |
| 140 | "@types/node": "catalog:", | 142 | "@types/node": "catalog:", |
| 141 | "@types/react": "^19.2.10", | 143 | "@types/react": "^19.2.10", |
| @@ -332,6 +334,29 @@ · 190 unmodified lines | |||
| 332 | "zod": "catalog:", | 334 | "zod": "catalog:", |
| 333 | }, | 335 | }, |
| 334 | }, | 336 | }, |
| 337 | "packages/platform/sdk-file": { | ||
| 338 | "name": "@executor/platform-sdk-file", | ||
| 339 | "version": "0.0.0", | ||
| 340 | "dependencies": { | ||
| 341 | "@effect/platform": "catalog:", | ||
| 342 | "@effect/platform-node": "catalog:", | ||
| 343 | "@executor/codemode-core": "workspace:*", | ||
| 344 | "@executor/ir": "workspace:*", | ||
| 345 | "@executor/platform-sdk": "workspace:*", | ||
| 346 | "@executor/source-core": "workspace:*", | ||
| 347 | "@executor/source-graphql": "workspace:*", | ||
| 348 | "@executor/source-openapi": "workspace:*", | ||
| 349 | "effect": "catalog:", | ||
| 350 | "jsonc-parser": "3.3.1", | ||
| 351 | }, | ||
| 352 | "devDependencies": { | ||
| 353 | "@effect/vitest": "catalog:", | ||
| 354 | "@types/node": "catalog:", | ||
| 355 | "bun-types": "catalog:", | ||
| 356 | "typescript": "^5.9.3", | ||
| 357 | "vitest": "catalog:", | ||
| 358 | }, | ||
| 359 | }, | ||
| 335 | "packages/platform/server": { | 360 | "packages/platform/server": { |
| 336 | "name": "@executor/server", | 361 | "name": "@executor/server", |
| 337 | "version": "0.0.0", | 362 | "version": "0.0.0", |
| @@ -343,6 +368,7 @@ · 5 unmodified lines | |||
| 343 | "@executor/platform-api": "workspace:*", | 368 | "@executor/platform-api": "workspace:*", |
| 344 | "@executor/platform-internal": "workspace:*", | 369 | "@executor/platform-internal": "workspace:*", |
| 345 | "@executor/platform-sdk": "workspace:*", | 370 | "@executor/platform-sdk": "workspace:*", |
| 371 | "@executor/platform-sdk-file": "workspace:*", | ||
| 346 | "@opentelemetry/api": "^1.9.0", | 372 | "@opentelemetry/api": "^1.9.0", |
| 347 | "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0", | 373 | "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0", |
| 348 | "@opentelemetry/resources": "^2.6.0", | 374 | "@opentelemetry/resources": "^2.6.0", |
| @@ -684,6 +710,8 @@ · 335 unmodified lines | |||
| 684 | 710 | ||
| 685 | "@executor/platform-sdk": ["@executor/platform-sdk@workspace:packages/platform/sdk"], | 711 | "@executor/platform-sdk": ["@executor/platform-sdk@workspace:packages/platform/sdk"], |
| 686 | 712 | ||
| 713 | "@executor/platform-sdk-file": ["@executor/platform-sdk-file@workspace:packages/platform/sdk-file"], | ||
| 714 | |||
| 687 | "@executor/react": ["@executor/react@workspace:packages/clients/react"], | 715 | "@executor/react": ["@executor/react@workspace:packages/clients/react"], |
| 688 | 716 | ||
| 689 | "@executor/runtime-deno-subprocess": ["@executor/runtime-deno-subprocess@workspace:packages/kernel/runtime-deno-subprocess"], | 717 | "@executor/runtime-deno-subprocess": ["@executor/runtime-deno-subprocess@workspace:packages/kernel/runtime-deno-subprocess"], |
| @@ -20,13 +20,22 @@ import * as Schema from "effect/Schema"; · 19 unmodified lines | |||
| 20 | 20 | ||
| 21 | import { | 21 | import { |
| 22 | ControlPlaneStore, | 22 | ControlPlaneStore, |
| 23 | LocalInstanceConfigService, | ||
| 24 | SecretMaterialDeleterService, | ||
| 25 | SecretMaterialStorerService, | ||
| 26 | SecretMaterialUpdaterService, | ||
| 23 | type ControlPlaneStoreShape, | 27 | type ControlPlaneStoreShape, |
| 24 | type WorkspaceInternalToolContext, | 28 | type WorkspaceInternalToolContext, |
| 25 | RuntimeLocalWorkspaceState, | ||
| 26 | RuntimeSourceAuthService, | 29 | RuntimeSourceAuthService, |
| 27 | RuntimeSourceCatalogSyncService, | 30 | RuntimeSourceCatalogSyncService, |
| 28 | RuntimeSourceStore, | 31 | RuntimeSourceStore, |
| 29 | RuntimeSourceStoreService, | 32 | RuntimeSourceStoreService, |
| 33 | } from "@executor/platform-sdk/runtime"; | ||
| 34 | import { | ||
| 35 | type RuntimeLocalWorkspaceState, | ||
| 36 | provideOptionalRuntimeLocalWorkspace, | ||
| 37 | } from "../../../sdk/src/runtime/workspace/runtime-context"; | ||
| 38 | import { | ||
| 30 | SourceArtifactStore, | 39 | SourceArtifactStore, |
| 31 | type SourceArtifactStoreShape, | 40 | type SourceArtifactStoreShape, |
| 32 | WorkspaceConfigStore, | 41 | WorkspaceConfigStore, |
| @@ -34,8 +43,7 @@ import { · 1 unmodified line | |||
| 34 | WorkspaceStateStore, | 43 | WorkspaceStateStore, |
| 35 | type WorkspaceStateStoreShape, | 44 | type WorkspaceStateStoreShape, |
| 36 | makeWorkspaceStorageLayer, | 45 | makeWorkspaceStorageLayer, |
| 37 | provideOptionalRuntimeLocalWorkspace, | 46 | } from "../../../sdk/src/runtime/workspace/storage"; |
| 38 | } from "@executor/platform-sdk/runtime"; | ||
| 39 | import { | 47 | import { |
| 40 | CreateSecretPayloadSchema, | 48 | CreateSecretPayloadSchema, |
| 41 | CreateSecretResultSchema, | 49 | CreateSecretResultSchema, |
| @@ -53,7 +61,6 @@ import { · 11 unmodified lines | |||
| 53 | deleteLocalSecret, | 61 | deleteLocalSecret, |
| 54 | discoverSource, | 62 | discoverSource, |
| 55 | discoverSourceInspectionTools, | 63 | discoverSourceInspectionTools, |
| 56 | getLocalInstanceConfig, | ||
| 57 | getPolicy, | 64 | getPolicy, |
| 58 | getSource, | 65 | getSource, |
| 59 | getSourceInspection, | 66 | getSourceInspection, |
| @@ -201,6 +208,10 @@ const makeRuntimeLayer = (input: { · 141 unmodified lines | |||
| 201 | workspaceConfigStore: WorkspaceConfigStoreShape; | 208 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 202 | workspaceStateStore: WorkspaceStateStoreShape; | 209 | workspaceStateStore: WorkspaceStateStoreShape; |
| 203 | sourceArtifactStore: SourceArtifactStoreShape; | 210 | sourceArtifactStore: SourceArtifactStoreShape; |
| 211 | instanceConfigResolver: WorkspaceInternalToolContext["instanceConfigResolver"]; | ||
| 212 | storeSecretMaterial: WorkspaceInternalToolContext["storeSecretMaterial"]; | ||
| 213 | deleteSecretMaterial: WorkspaceInternalToolContext["deleteSecretMaterial"]; | ||
| 214 | updateSecretMaterial: WorkspaceInternalToolContext["updateSecretMaterial"]; | ||
| 204 | }) => | 215 | }) => |
| 205 | Layer.mergeAll( | 216 | Layer.mergeAll( |
| 206 | Layer.succeed(ControlPlaneStore, input.controlPlaneStore), | 217 | Layer.succeed(ControlPlaneStore, input.controlPlaneStore), |
| @@ -209,6 +220,10 @@ const makeRuntimeLayer = (input: { · 2 unmodified lines | |||
| 209 | RuntimeSourceCatalogSyncService, | 220 | RuntimeSourceCatalogSyncService, |
| 210 | input.sourceCatalogSyncService, | 221 | input.sourceCatalogSyncService, |
| 211 | ), | 222 | ), |
| 223 | Layer.succeed(LocalInstanceConfigService, input.instanceConfigResolver), | ||
| 224 | Layer.succeed(SecretMaterialStorerService, input.storeSecretMaterial), | ||
| 225 | Layer.succeed(SecretMaterialDeleterService, input.deleteSecretMaterial), | ||
| 226 | Layer.succeed(SecretMaterialUpdaterService, input.updateSecretMaterial), | ||
| 212 | makeWorkspaceStorageLayer({ | 227 | makeWorkspaceStorageLayer({ |
| 213 | workspaceConfigStore: input.workspaceConfigStore, | 228 | workspaceConfigStore: input.workspaceConfigStore, |
| 214 | workspaceStateStore: input.workspaceStateStore, | 229 | workspaceStateStore: input.workspaceStateStore, |
| @@ -220,8 +235,12 @@ const runRuntimeEffect = <A, E, R>(input: { · 5 unmodified lines | |||
| 220 | effect: Effect.Effect<A, E, R>; | 235 | effect: Effect.Effect<A, E, R>; |
| 221 | runtimeLayer: Layer.Layer< | 236 | runtimeLayer: Layer.Layer< |
| 222 | | ControlPlaneStore | 237 | | ControlPlaneStore |
| 238 | | LocalInstanceConfigService | ||
| 223 | | RuntimeSourceStoreService | 239 | | RuntimeSourceStoreService |
| 224 | | RuntimeSourceCatalogSyncService | 240 | | RuntimeSourceCatalogSyncService |
| 241 | | SecretMaterialStorerService | ||
| 242 | | SecretMaterialDeleterService | ||
| 243 | | SecretMaterialUpdaterService | ||
| 225 | | WorkspaceConfigStore | 244 | | WorkspaceConfigStore |
| 226 | | WorkspaceStateStore | 245 | | WorkspaceStateStore |
| 227 | | SourceArtifactStore, | 246 | | SourceArtifactStore, |
| @@ -263,6 +282,10 @@ export const createWorkspaceExecutorAdminToolMap = ( · 35 unmodified lines | |||
| 263 | workspaceConfigStore: input.workspaceConfigStore, | 282 | workspaceConfigStore: input.workspaceConfigStore, |
| 264 | workspaceStateStore: input.workspaceStateStore, | 283 | workspaceStateStore: input.workspaceStateStore, |
| 265 | sourceArtifactStore: input.sourceArtifactStore, | 284 | sourceArtifactStore: input.sourceArtifactStore, |
| 285 | instanceConfigResolver: input.instanceConfigResolver, | ||
| 286 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 287 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 288 | updateSecretMaterial: input.updateSecretMaterial, | ||
| 266 | }); | 289 | }); |
| 267 | const workspaceStorageLayer = makeWorkspaceStorageLayer({ | 290 | const workspaceStorageLayer = makeWorkspaceStorageLayer({ |
| 268 | workspaceConfigStore: input.workspaceConfigStore, | 291 | workspaceConfigStore: input.workspaceConfigStore, |
| @@ -295,7 +318,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 26 unmodified lines | |||
| 295 | "Get local instance config such as supported secret providers.", | 318 | "Get local instance config such as supported secret providers.", |
| 296 | inputSchema: emptyInputSchema, | 319 | inputSchema: emptyInputSchema, |
| 297 | outputSchema: instanceConfigOutputSchema, | 320 | outputSchema: instanceConfigOutputSchema, |
| 298 | execute: () => Effect.runPromise(getLocalInstanceConfig()), | 321 | execute: () => Effect.runPromise(input.instanceConfigResolver()), |
| 299 | }, | 322 | }, |
| 300 | metadata, | 323 | metadata, |
| 301 | }), | 324 | }), |
| @@ -0,0 +1,33 @@ | |||
| 1 | { | ||
| 2 | "name": "@executor/platform-sdk-file", | ||
| 3 | "private": true, | ||
| 4 | "type": "module", | ||
| 5 | "exports": { | ||
| 6 | ".": "./src/index.ts" | ||
| 7 | }, | ||
| 8 | "scripts": { | ||
| 9 | "fixture:release:capture": "bun run ./src/capture-release-workspace-fixture.ts", | ||
| 10 | "test": "vitest run", | ||
| 11 | "typecheck": "bunx tsc --noEmit -p tsconfig.json" | ||
| 12 | }, | ||
| 13 | "dependencies": { | ||
| 14 | "@effect/platform": "catalog:", | ||
| 15 | "@effect/platform-node": "catalog:", | ||
| 16 | "@executor/codemode-core": "workspace:*", | ||
| 17 | "@executor/source-core": "workspace:*", | ||
| 18 | "@executor/source-graphql": "workspace:*", | ||
| 19 | "@executor/source-openapi": "workspace:*", | ||
| 20 | "@executor/ir": "workspace:*", | ||
| 21 | "@executor/platform-sdk": "workspace:*", | ||
| 22 | "effect": "catalog:", | ||
| 23 | "jsonc-parser": "3.3.1" | ||
| 24 | }, | ||
| 25 | "devDependencies": { | ||
| 26 | "@effect/vitest": "catalog:", | ||
| 27 | "@types/node": "catalog:", | ||
| 28 | "bun-types": "catalog:", | ||
| 29 | "typescript": "^5.9.3", | ||
| 30 | "vitest": "catalog:" | ||
| 31 | }, | ||
| 32 | "version": "0.0.0" | ||
| 33 | } | ||
| @@ -6,7 +6,7 @@ import * as Data from "effect/Data"; · 5 unmodified lines | |||
| 6 | import * as Effect from "effect/Effect"; | 6 | import * as Effect from "effect/Effect"; |
| 7 | import * as Exit from "effect/Exit"; | 7 | import * as Exit from "effect/Exit"; |
| 8 | 8 | ||
| 9 | import type { LocalExecutorConfig } from "#schema"; | 9 | import type { LocalExecutorConfig } from "@executor/platform-sdk/schema"; |
| 10 | 10 | ||
| 11 | import { | 11 | import { |
| 12 | readOptionalLocalExecutorConfig, | 12 | readOptionalLocalExecutorConfig, |
| @@ -37,7 +37,7 @@ type CaptureReleaseWorkspaceFixtureArgs = { · 24 unmodified lines | |||
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | const usage = `Usage: | 39 | const usage = `Usage: |
| 40 | bun run ./src/runtime/local/capture-release-workspace-fixture.ts \\ | 40 | bun run ./src/capture-release-workspace-fixture.ts \\ |
| 41 | --workspace-root /path/to/workspace \\ | 41 | --workspace-root /path/to/workspace \\ |
| 42 | --source-id google-calendar \\ | 42 | --source-id google-calendar \\ |
| 43 | --release-version v1.2.4-beta.1 \\ | 43 | --release-version v1.2.4-beta.1 \\ |
| @@ -58,7 +58,7 @@ describe("local-config", () => { · 57 unmodified lines | |||
| 58 | "https://api.github.com", | 58 | "https://api.github.com", |
| 59 | ); | 59 | ); |
| 60 | expect(loaded.config?.runtime).toBe("ses"); | 60 | expect(loaded.config?.runtime).toBe("ses"); |
| 61 | expect(context.homeStateDirectory).toContain("executor"); | 61 | expect(context.homeStateDirectory.toLowerCase()).toContain("executor"); |
| 62 | }).pipe(Effect.provide(NodeFileSystem.layer)), | 62 | }).pipe(Effect.provide(NodeFileSystem.layer)), |
| 63 | ); | 63 | ); |
| 64 | 64 | ||
| @@ -13,9 +13,11 @@ import { · 12 unmodified lines | |||
| 13 | type LocalConfigPolicy, | 13 | type LocalConfigPolicy, |
| 14 | type LocalConfigSecretProvider, | 14 | type LocalConfigSecretProvider, |
| 15 | type LocalConfigSource, | 15 | type LocalConfigSource, |
| 16 | } from "#schema"; | 16 | } from "@executor/platform-sdk/schema"; |
| 17 | import * as Effect from "effect/Effect"; | 17 | import * as Effect from "effect/Effect"; |
| 18 | import * as Schema from "effect/Schema"; | 18 | import * as Schema from "effect/Schema"; |
| 19 | import type { ExecutorWorkspaceDescriptor } from "@executor/platform-sdk"; | ||
| 20 | import type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/workspace-config"; | ||
| 19 | import { | 21 | import { |
| 20 | LocalExecutorConfigDecodeError, | 22 | LocalExecutorConfigDecodeError, |
| 21 | LocalFileSystemError, | 23 | LocalFileSystemError, |
| @@ -24,6 +26,33 @@ import { · 2 unmodified lines | |||
| 24 | 26 | ||
| 25 | const decodeLocalExecutorConfig = Schema.decodeUnknownSync(LocalExecutorConfigSchema); | 27 | const decodeLocalExecutorConfig = Schema.decodeUnknownSync(LocalExecutorConfigSchema); |
| 26 | 28 | ||
| 29 | export type FileLoadedExecutorConfig = LoadedLocalExecutorConfig & { | ||
| 30 | homeConfigPath: string; | ||
| 31 | projectConfigPath: string; | ||
| 32 | }; | ||
| 33 | |||
| 34 | export const encodeLocalExecutorConfig = (config: LocalExecutorConfig): string => | ||
| 35 | `${JSON.stringify(config, null, 2)}\n`; | ||
| 36 | |||
| 37 | export const resolveConfigRelativePath = (input: { | ||
| 38 | path: string; | ||
| 39 | workspaceRoot: string; | ||
| 40 | }): string => { | ||
| 41 | const trimmed = input.path.trim(); | ||
| 42 | if (trimmed.startsWith("~/")) { | ||
| 43 | return join(homedir(), trimmed.slice(2)); | ||
| 44 | } | ||
| 45 | if (trimmed === "~") { | ||
| 46 | return homedir(); | ||
| 47 | } | ||
| 48 | if (isAbsolute(trimmed)) { | ||
| 49 | return trimmed; | ||
| 50 | } | ||
| 51 | return resolve(input.workspaceRoot, trimmed); | ||
| 52 | }; | ||
| 53 | |||
| 54 | export type { LoadedLocalExecutorConfig } from "../../sdk/src/runtime/workspace-config"; | ||
| 55 | |||
| 27 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; | 56 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; |
| 28 | const PROJECT_CONFIG_DIRECTORY = ".executor"; | 57 | const PROJECT_CONFIG_DIRECTORY = ".executor"; |
| 29 | const EXECUTOR_CONFIG_DIR_ENV = "EXECUTOR_CONFIG_DIR"; | 58 | const EXECUTOR_CONFIG_DIR_ENV = "EXECUTOR_CONFIG_DIR"; |
| @@ -329,14 +358,6 @@ export type ResolvedLocalWorkspaceContext = { · 299 unmodified lines | |||
| 329 | stateDirectory: string; | 358 | stateDirectory: string; |
| 330 | }; | 359 | }; |
| 331 | 360 | ||
| 332 | export type LoadedLocalExecutorConfig = { | ||
| 333 | config: LocalExecutorConfig | null; | ||
| 334 | homeConfig: LocalExecutorConfig | null; | ||
| 335 | projectConfig: LocalExecutorConfig | null; | ||
| 336 | homeConfigPath: string; | ||
| 337 | projectConfigPath: string; | ||
| 338 | }; | ||
| 339 | |||
| 340 | export const resolveLocalWorkspaceContext = (input: { | 361 | export const resolveLocalWorkspaceContext = (input: { |
| 341 | cwd?: string; | 362 | cwd?: string; |
| 342 | workspaceRoot?: string; | 363 | workspaceRoot?: string; |
| @@ -409,7 +430,7 @@ export const readOptionalLocalExecutorConfig = ( · 66 unmodified lines | |||
| 409 | export const loadLocalExecutorConfig = ( | 430 | export const loadLocalExecutorConfig = ( |
| 410 | context: ResolvedLocalWorkspaceContext, | 431 | context: ResolvedLocalWorkspaceContext, |
| 411 | ): Effect.Effect< | 432 | ): Effect.Effect< |
| 412 | LoadedLocalExecutorConfig, | 433 | FileLoadedExecutorConfig, |
| 413 | LocalFileSystemError | LocalExecutorConfigDecodeError, | 434 | LocalFileSystemError | LocalExecutorConfigDecodeError, |
| 414 | FileSystem.FileSystem | 435 | FileSystem.FileSystem |
| 415 | > => | 436 | > => |
| @@ -428,9 +449,6 @@ export const loadLocalExecutorConfig = ( · 12 unmodified lines | |||
| 428 | }; | 449 | }; |
| 429 | }); | 450 | }); |
| 430 | 451 | ||
| 431 | export const encodeLocalExecutorConfig = (config: LocalExecutorConfig): string => | ||
| 432 | `${JSON.stringify(config, null, 2)}\n`; | ||
| 433 | |||
| 434 | export const writeProjectLocalExecutorConfig = (input: { | 452 | export const writeProjectLocalExecutorConfig = (input: { |
| 435 | context: ResolvedLocalWorkspaceContext; | 453 | context: ResolvedLocalWorkspaceContext; |
| 436 | config: LocalExecutorConfig; | 454 | config: LocalExecutorConfig; |
| @@ -448,22 +466,5 @@ export const writeProjectLocalExecutorConfig = (input: { · 11 unmodified lines | |||
| 448 | ); | 466 | ); |
| 449 | }); | 467 | }); |
| 450 | 468 | ||
| 451 | export const resolveConfigRelativePath = (input: { | ||
| 452 | path: string; | ||
| 453 | workspaceRoot: string; | ||
| 454 | }): string => { | ||
| 455 | const trimmed = input.path.trim(); | ||
| 456 | if (trimmed.startsWith("~/")) { | ||
| 457 | return join(homedir(), trimmed.slice(2)); | ||
| 458 | } | ||
| 459 | if (trimmed === "~") { | ||
| 460 | return homedir(); | ||
| 461 | } | ||
| 462 | if (isAbsolute(trimmed)) { | ||
| 463 | return trimmed; | ||
| 464 | } | ||
| 465 | return resolve(input.workspaceRoot, trimmed); | ||
| 466 | }; | ||
| 467 | |||
| 468 | export const defaultWorkspaceDisplayName = (context: ResolvedLocalWorkspaceContext): string => | 469 | export const defaultWorkspaceDisplayName = (context: ResolvedLocalWorkspaceContext): string => |
| 469 | trimOrUndefined(context.workspaceName) ?? "workspace"; | 470 | trimOrUndefined(context.workspaceName) ?? "workspace"; |
File renamed without changes.
| @@ -24,7 +24,7 @@ import { · 23 unmodified lines | |||
| 24 | WorkspaceOauthClientSchema, | 24 | WorkspaceOauthClientSchema, |
| 25 | type WorkspaceSourceOauthClient, | 25 | type WorkspaceSourceOauthClient, |
| 26 | WorkspaceSourceOauthClientSchema, | 26 | WorkspaceSourceOauthClientSchema, |
| 27 | } from "#schema"; | 27 | } from "@executor/platform-sdk/schema"; |
| 28 | import * as Effect from "effect/Effect"; | 28 | import * as Effect from "effect/Effect"; |
| 29 | import * as Option from "effect/Option"; | 29 | import * as Option from "effect/Option"; |
| 30 | import * as Schema from "effect/Schema"; | 30 | import * as Schema from "effect/Schema"; |
File renamed without changes.
| @@ -0,0 +1,364 @@ | |||
| 1 | import { FileSystem } from "@effect/platform"; | ||
| 2 | import { NodeFileSystem } from "@effect/platform-node"; | ||
| 3 | import { | ||
| 4 | createExecutor, | ||
| 5 | createExecutorEffect, | ||
| 6 | type CreateExecutorOptions, | ||
| 7 | type Executor, | ||
| 8 | type ExecutorBackend, | ||
| 9 | type ExecutorBackendServices, | ||
| 10 | type ExecutorRuntimeOptions, | ||
| 11 | type ExecutorWorkspaceDescriptor, | ||
| 12 | } from "@executor/platform-sdk"; | ||
| 13 | import type { | ||
| 14 | LocalExecutorConfig, | ||
| 15 | LocalInstallation, | ||
| 16 | } from "@executor/platform-sdk/schema"; | ||
| 17 | import { | ||
| 18 | type RuntimeControlPlaneOptions, | ||
| 19 | type ControlPlaneRuntime, | ||
| 20 | type ResolveSecretMaterial, | ||
| 21 | createControlPlaneRuntimeFromServices, | ||
| 22 | } from "@executor/platform-sdk/runtime"; | ||
| 23 | import * as Effect from "effect/Effect"; | ||
| 24 | import type { LocalToolRuntime } from "../../sdk/src/runtime/local-tool-runtime"; | ||
| 25 | import { | ||
| 26 | type LoadedLocalExecutorConfig, | ||
| 27 | type ResolvedLocalWorkspaceContext, | ||
| 28 | loadLocalExecutorConfig, | ||
| 29 | resolveConfigRelativePath, | ||
| 30 | resolveLocalWorkspaceContext, | ||
| 31 | writeProjectLocalExecutorConfig, | ||
| 32 | } from "./config"; | ||
| 33 | import { createLocalControlPlanePersistence } from "./control-plane-store"; | ||
| 34 | import { | ||
| 35 | createDefaultSecretMaterialDeleter, | ||
| 36 | createLocalInstanceConfigResolver, | ||
| 37 | createDefaultSecretMaterialResolver, | ||
| 38 | createDefaultSecretMaterialStorer, | ||
| 39 | createDefaultSecretMaterialUpdater, | ||
| 40 | } from "./secret-material-providers"; | ||
| 41 | import { | ||
| 42 | buildLocalSourceArtifact, | ||
| 43 | readLocalSourceArtifact, | ||
| 44 | removeLocalSourceArtifact, | ||
| 45 | writeLocalSourceArtifact, | ||
| 46 | } from "./source-artifacts"; | ||
| 47 | import { | ||
| 48 | getOrProvisionLocalInstallation, | ||
| 49 | loadLocalInstallation, | ||
| 50 | } from "./installation"; | ||
| 51 | import { | ||
| 52 | loadLocalToolRuntime, | ||
| 53 | } from "./tools"; | ||
| 54 | import { | ||
| 55 | loadLocalWorkspaceState, | ||
| 56 | writeLocalWorkspaceState, | ||
| 57 | } from "./workspace-state"; | ||
| 58 | import { | ||
| 59 | refreshSourceTypeDeclarationInBackground, | ||
| 60 | refreshWorkspaceSourceTypeDeclarationsInBackground, | ||
| 61 | } from "./source-type-declarations"; | ||
| 62 | |||
| 63 | const toError = (cause: unknown): Error => | ||
| 64 | cause instanceof Error ? cause : new Error(String(cause)); | ||
| 65 | |||
| 66 | const bindFileSystem = <A, E>( | ||
| 67 | fileSystem: FileSystem.FileSystem, | ||
| 68 | effect: Effect.Effect<A, E, FileSystem.FileSystem>, | ||
| 69 | ): Effect.Effect<A, E, never> => | ||
| 70 | effect.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem)); | ||
| 71 | |||
| 72 | export type CreateLocalExecutorBackendOptions = { | ||
| 73 | cwd?: string; | ||
| 74 | workspaceRoot?: string; | ||
| 75 | homeConfigPath?: string; | ||
| 76 | homeStateDirectory?: string; | ||
| 77 | localDataDir?: string; | ||
| 78 | }; | ||
| 79 | |||
| 80 | type CreateLocalRuntimeOptions = CreateLocalExecutorBackendOptions & | ||
| 81 | RuntimeControlPlaneOptions; | ||
| 82 | |||
| 83 | type LocalWorkspaceState = Parameters< | ||
| 84 | typeof writeLocalWorkspaceState | ||
| 85 | >[0]["state"]; | ||
| 86 | |||
| 87 | type LocalSourceArtifact = Parameters< | ||
| 88 | typeof writeLocalSourceArtifact | ||
| 89 | >[0]["artifact"]; | ||
| 90 | |||
| 91 | const createBoundInstallationStore = ( | ||
| 92 | context: ResolvedLocalWorkspaceContext, | ||
| 93 | ): { | ||
| 94 | load: () => Effect.Effect<LocalInstallation, Error, never>; | ||
| 95 | getOrProvision: () => Effect.Effect<LocalInstallation, Error, never>; | ||
| 96 | } => ({ | ||
| 97 | load: () => loadLocalInstallation(context).pipe(Effect.mapError(toError)), | ||
| 98 | getOrProvision: () => | ||
| 99 | getOrProvisionLocalInstallation({ context }).pipe( | ||
| 100 | Effect.mapError(toError), | ||
| 101 | ), | ||
| 102 | }); | ||
| 103 | |||
| 104 | const createBoundWorkspaceConfigStore = ( | ||
| 105 | context: ResolvedLocalWorkspaceContext, | ||
| 106 | fileSystem: FileSystem.FileSystem, | ||
| 107 | ): { | ||
| 108 | load: () => Effect.Effect<LoadedLocalExecutorConfig, Error, never>; | ||
| 109 | writeProject: (config: LocalExecutorConfig) => Effect.Effect<void, Error, never>; | ||
| 110 | resolveRelativePath: typeof resolveConfigRelativePath; | ||
| 111 | } => ({ | ||
| 112 | load: () => | ||
| 113 | bindFileSystem(fileSystem, loadLocalExecutorConfig(context)).pipe( | ||
| 114 | Effect.mapError(toError), | ||
| 115 | ), | ||
| 116 | writeProject: (config) => | ||
| 117 | bindFileSystem( | ||
| 118 | fileSystem, | ||
| 119 | writeProjectLocalExecutorConfig({ context, config }), | ||
| 120 | ).pipe(Effect.mapError(toError)), | ||
| 121 | resolveRelativePath: resolveConfigRelativePath, | ||
| 122 | }); | ||
| 123 | |||
| 124 | const createBoundWorkspaceStateStore = ( | ||
| 125 | context: ResolvedLocalWorkspaceContext, | ||
| 126 | fileSystem: FileSystem.FileSystem, | ||
| 127 | ): { | ||
| 128 | load: () => Effect.Effect<LocalWorkspaceState, Error, never>; | ||
| 129 | write: (state: LocalWorkspaceState) => Effect.Effect<void, Error, never>; | ||
| 130 | } => ({ | ||
| 131 | load: () => | ||
| 132 | bindFileSystem(fileSystem, loadLocalWorkspaceState(context)).pipe( | ||
| 133 | Effect.mapError(toError), | ||
| 134 | ), | ||
| 135 | write: (state) => | ||
| 136 | bindFileSystem( | ||
| 137 | fileSystem, | ||
| 138 | writeLocalWorkspaceState({ context, state }), | ||
| 139 | ).pipe(Effect.mapError(toError)), | ||
| 140 | }); | ||
| 141 | |||
| 142 | const createBoundSourceArtifactStore = ( | ||
| 143 | context: ResolvedLocalWorkspaceContext, | ||
| 144 | fileSystem: FileSystem.FileSystem, | ||
| 145 | ): { | ||
| 146 | build: typeof buildLocalSourceArtifact; | ||
| 147 | read: (sourceId: string) => Effect.Effect<LocalSourceArtifact | null, Error, never>; | ||
| 148 | write: (input: { | ||
| 149 | sourceId: string; | ||
| 150 | artifact: LocalSourceArtifact; | ||
| 151 | }) => Effect.Effect<void, Error, never>; | ||
| 152 | remove: (sourceId: string) => Effect.Effect<void, Error, never>; | ||
| 153 | } => ({ | ||
| 154 | build: buildLocalSourceArtifact, | ||
| 155 | read: (sourceId) => | ||
| 156 | bindFileSystem( | ||
| 157 | fileSystem, | ||
| 158 | readLocalSourceArtifact({ context, sourceId }), | ||
| 159 | ).pipe(Effect.mapError(toError)), | ||
| 160 | write: ({ sourceId, artifact }) => | ||
| 161 | bindFileSystem( | ||
| 162 | fileSystem, | ||
| 163 | writeLocalSourceArtifact({ context, sourceId, artifact }), | ||
| 164 | ).pipe(Effect.mapError(toError)), | ||
| 165 | remove: (sourceId) => | ||
| 166 | bindFileSystem( | ||
| 167 | fileSystem, | ||
| 168 | removeLocalSourceArtifact({ context, sourceId }), | ||
| 169 | ).pipe(Effect.mapError(toError)), | ||
| 170 | }); | ||
| 171 | |||
| 172 | const createBoundLocalToolRuntimeLoader = ( | ||
| 173 | context: ResolvedLocalWorkspaceContext, | ||
| 174 | fileSystem: FileSystem.FileSystem, | ||
| 175 | ): ExecutorBackendServices["localToolRuntimeLoader"] => ({ | ||
| 176 | load: () => | ||
| 177 | bindFileSystem(fileSystem, loadLocalToolRuntime(context)), | ||
| 178 | }); | ||
| 179 | |||
| 180 | const createBoundSourceTypeDeclarationsRefresher = ( | ||
| 181 | context: ResolvedLocalWorkspaceContext, | ||
| 182 | ): NonNullable<ExecutorBackendServices["sourceTypeDeclarationsRefresher"]> => ({ | ||
| 183 | refreshWorkspaceInBackground: ({ entries }) => | ||
| 184 | Effect.sync(() => { | ||
| 185 | refreshWorkspaceSourceTypeDeclarationsInBackground({ | ||
| 186 | context, | ||
| 187 | entries, | ||
| 188 | }); | ||
| 189 | }), | ||
| 190 | refreshSourceInBackground: ({ source, snapshot }) => | ||
| 191 | Effect.sync(() => { | ||
| 192 | refreshSourceTypeDeclarationInBackground({ | ||
| 193 | context, | ||
| 194 | source, | ||
| 195 | snapshot, | ||
| 196 | }); | ||
| 197 | }), | ||
| 198 | }); | ||
| 199 | |||
| 200 | const toExecutorWorkspaceContext = ( | ||
| 201 | context: ResolvedLocalWorkspaceContext, | ||
| 202 | ): ExecutorWorkspaceDescriptor => ({ | ||
| 203 | workspaceName: context.workspaceName, | ||
| 204 | workspaceRoot: context.workspaceRoot, | ||
| 205 | metadata: { | ||
| 206 | kind: "file", | ||
| 207 | configDirectory: context.configDirectory, | ||
| 208 | projectConfigPath: context.projectConfigPath, | ||
| 209 | homeConfigPath: context.homeConfigPath, | ||
| 210 | homeStateDirectory: context.homeStateDirectory, | ||
| 211 | artifactsDirectory: context.artifactsDirectory, | ||
| 212 | stateDirectory: context.stateDirectory, | ||
| 213 | }, | ||
| 214 | }); | ||
| 215 | |||
| 216 | export const createLocalExecutorServicesEffect = ( | ||
| 217 | options: CreateLocalExecutorBackendOptions = {}, | ||
| 218 | runtimeOptions: RuntimeControlPlaneOptions = {}, | ||
| 219 | ): Effect.Effect<ExecutorBackendServices, Error> => | ||
| 220 | Effect.gen(function* () { | ||
| 221 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 222 | const workspaceContext = yield* bindFileSystem( | ||
| 223 | fileSystem, | ||
| 224 | resolveLocalWorkspaceContext({ | ||
| 225 | cwd: options.cwd, | ||
| 226 | workspaceRoot: options.workspaceRoot, | ||
| 227 | homeConfigPath: options.homeConfigPath, | ||
| 228 | homeStateDirectory: options.homeStateDirectory, | ||
| 229 | }), | ||
| 230 | ).pipe(Effect.mapError(toError)); | ||
| 231 | |||
| 232 | const persistence = createLocalControlPlanePersistence( | ||
| 233 | workspaceContext, | ||
| 234 | fileSystem, | ||
| 235 | ); | ||
| 236 | const workspaceConfigStore = createBoundWorkspaceConfigStore( | ||
| 237 | workspaceContext, | ||
| 238 | fileSystem, | ||
| 239 | ); | ||
| 240 | const loadedConfig = yield* workspaceConfigStore.load(); | ||
| 241 | const resolveSecretMaterial: ResolveSecretMaterial = | ||
| 242 | runtimeOptions.resolveSecretMaterial | ||
| 243 | ?? createDefaultSecretMaterialResolver({ | ||
| 244 | rows: persistence.rows, | ||
| 245 | localConfig: loadedConfig.config, | ||
| 246 | workspaceRoot: workspaceContext.workspaceRoot, | ||
| 247 | }); | ||
| 248 | |||
| 249 | return { | ||
| 250 | workspace: toExecutorWorkspaceContext(workspaceContext), | ||
| 251 | installationStore: createBoundInstallationStore(workspaceContext), | ||
| 252 | workspaceConfigStore, | ||
| 253 | workspaceStateStore: createBoundWorkspaceStateStore( | ||
| 254 | workspaceContext, | ||
| 255 | fileSystem, | ||
| 256 | ), | ||
| 257 | sourceArtifactStore: createBoundSourceArtifactStore( | ||
| 258 | workspaceContext, | ||
| 259 | fileSystem, | ||
| 260 | ), | ||
| 261 | localToolRuntimeLoader: createBoundLocalToolRuntimeLoader( | ||
| 262 | workspaceContext, | ||
| 263 | fileSystem, | ||
| 264 | ), | ||
| 265 | sourceTypeDeclarationsRefresher: | ||
| 266 | createBoundSourceTypeDeclarationsRefresher(workspaceContext), | ||
| 267 | secretMaterial: { | ||
| 268 | resolve: resolveSecretMaterial, | ||
| 269 | store: createDefaultSecretMaterialStorer({ | ||
| 270 | rows: persistence.rows, | ||
| 271 | }), | ||
| 272 | delete: createDefaultSecretMaterialDeleter({ | ||
| 273 | rows: persistence.rows, | ||
| 274 | }), | ||
| 275 | update: createDefaultSecretMaterialUpdater({ | ||
| 276 | rows: persistence.rows, | ||
| 277 | }), | ||
| 278 | }, | ||
| 279 | instanceConfig: { | ||
| 280 | resolve: createLocalInstanceConfigResolver(), | ||
| 281 | }, | ||
| 282 | persistence, | ||
| 283 | } satisfies ExecutorBackendServices; | ||
| 284 | }).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 285 | |||
| 286 | export const createLocalExecutorBackend = ( | ||
| 287 | options: CreateLocalExecutorBackendOptions = {}, | ||
| 288 | ): ExecutorBackend => ({ | ||
| 289 | createRuntime: (runtimeOptions) => | ||
| 290 | Effect.flatMap( | ||
| 291 | createLocalExecutorServicesEffect(options, runtimeOptions), | ||
| 292 | (services) => | ||
| 293 | createControlPlaneRuntimeFromServices({ | ||
| 294 | ...runtimeOptions, | ||
| 295 | services, | ||
| 296 | }), | ||
| 297 | ).pipe(Effect.provide(NodeFileSystem.layer)), | ||
| 298 | }); | ||
| 299 | |||
| 300 | export const createLocalControlPlaneRuntime = ( | ||
| 301 | options: CreateLocalRuntimeOptions = {}, | ||
| 302 | ): Effect.Effect<ControlPlaneRuntime, Error> => | ||
| 303 | Effect.flatMap( | ||
| 304 | createLocalExecutorServicesEffect(options, options), | ||
| 305 | (services) => | ||
| 306 | createControlPlaneRuntimeFromServices({ | ||
| 307 | executionResolver: options.executionResolver, | ||
| 308 | createInternalToolMap: options.createInternalToolMap, | ||
| 309 | resolveSecretMaterial: options.resolveSecretMaterial, | ||
| 310 | getLocalServerBaseUrl: options.getLocalServerBaseUrl, | ||
| 311 | services, | ||
| 312 | }), | ||
| 313 | ).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 314 | |||
| 315 | export const createLocalExecutorEffect = ( | ||
| 316 | options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {}, | ||
| 317 | ): Effect.Effect<Executor, Error> => | ||
| 318 | createExecutorEffect({ | ||
| 319 | backend: createLocalExecutorBackend(options), | ||
| 320 | executionResolver: options.executionResolver, | ||
| 321 | createInternalToolMap: options.createInternalToolMap, | ||
| 322 | resolveSecretMaterial: options.resolveSecretMaterial, | ||
| 323 | getLocalServerBaseUrl: options.getLocalServerBaseUrl, | ||
| 324 | } satisfies CreateExecutorOptions); | ||
| 325 | |||
| 326 | export const createLocalExecutor = ( | ||
| 327 | options: CreateLocalExecutorBackendOptions & ExecutorRuntimeOptions = {}, | ||
| 328 | ): Promise<Executor> => Effect.runPromise(createLocalExecutorEffect(options)); | ||
| 329 | |||
| 330 | export { | ||
| 331 | deriveLocalInstallation, | ||
| 332 | getOrProvisionLocalInstallation, | ||
| 333 | loadLocalInstallation, | ||
| 334 | } from "./installation"; | ||
| 335 | export { | ||
| 336 | loadLocalControlPlaneState, | ||
| 337 | writeLocalControlPlaneState, | ||
| 338 | } from "./control-plane-store"; | ||
| 339 | export { | ||
| 340 | loadLocalExecutorConfig, | ||
| 341 | resolveConfigRelativePath, | ||
| 342 | resolveLocalWorkspaceContext, | ||
| 343 | writeProjectLocalExecutorConfig, | ||
| 344 | } from "./config"; | ||
| 345 | export { | ||
| 346 | loadLocalWorkspaceState, | ||
| 347 | writeLocalWorkspaceState, | ||
| 348 | } from "./workspace-state"; | ||
| 349 | export { | ||
| 350 | buildLocalSourceArtifact, | ||
| 351 | readLocalSourceArtifact, | ||
| 352 | removeLocalSourceArtifact, | ||
| 353 | writeLocalSourceArtifact, | ||
| 354 | } from "./source-artifacts"; | ||
| 355 | export { | ||
| 356 | refreshSourceTypeDeclarationInBackground, | ||
| 357 | refreshWorkspaceSourceTypeDeclarationsInBackground, | ||
| 358 | syncSourceTypeDeclarationNode, | ||
| 359 | syncWorkspaceSourceTypeDeclarationsNode, | ||
| 360 | } from "./source-type-declarations"; | ||
| 361 | export type { | ||
| 362 | LoadedLocalExecutorConfig, | ||
| 363 | ResolvedLocalWorkspaceContext, | ||
| 364 | } from "./config"; | ||
| @@ -5,7 +5,7 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines | |||
| 5 | import { describe, expect, it } from "@effect/vitest"; | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | import * as Effect from "effect/Effect"; | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | ||
| 8 | import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../../sdk-file/src/index"; | 8 | import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "./index"; |
| 9 | import { getOrProvisionLocalInstallation } from "./installation"; | 9 | import { getOrProvisionLocalInstallation } from "./installation"; |
| 10 | import { resolveLocalWorkspaceContext } from "./config"; | 10 | import { resolveLocalWorkspaceContext } from "./config"; |
| 11 | 11 | ||
| @@ -4,7 +4,7 @@ import { · 3 unmodified lines | |||
| 4 | AccountIdSchema, | 4 | AccountIdSchema, |
| 5 | WorkspaceIdSchema, | 5 | WorkspaceIdSchema, |
| 6 | type LocalInstallation, | 6 | type LocalInstallation, |
| 7 | } from "#schema"; | 7 | } from "@executor/platform-sdk/schema"; |
| 8 | import * as Effect from "effect/Effect"; | 8 | import * as Effect from "effect/Effect"; |
| 9 | 9 | ||
| 10 | import type { ResolvedLocalWorkspaceContext } from "./config"; | 10 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| @@ -28,7 +28,7 @@ export type ReleaseWorkspaceFixture = ReleaseWorkspaceFixtureManifest & { · 27 unmodified lines | |||
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | export const releaseWorkspaceFixturesRoot = fileURLToPath( | 30 | export const releaseWorkspaceFixturesRoot = fileURLToPath( |
| 31 | new URL("../__fixtures__", import.meta.url), | 31 | new URL("../../sdk/src/runtime/__fixtures__", import.meta.url), |
| 32 | ); | 32 | ); |
| 33 | 33 | ||
| 34 | const decodeReleaseWorkspaceFixtureManifest = Schema.decodeUnknownSync( | 34 | const decodeReleaseWorkspaceFixtureManifest = Schema.decodeUnknownSync( |
| @@ -7,22 +7,34 @@ import { NodeFileSystem } from "@effect/platform-node"; · 6 unmodified lines | |||
| 7 | import { | 7 | import { |
| 8 | type LocalConfigSecretProvider, | 8 | type LocalConfigSecretProvider, |
| 9 | type LocalExecutorConfig, | 9 | type LocalExecutorConfig, |
| 10 | type SecretMaterial, | ||
| 11 | type SecretMaterialPurpose, | 10 | type SecretMaterialPurpose, |
| 12 | SecretMaterialIdSchema, | ||
| 13 | type SecretRef, | 11 | type SecretRef, |
| 14 | } from "#schema"; | 12 | type SecretMaterial, |
| 15 | import * as Context from "effect/Context"; | 13 | SecretMaterialIdSchema, |
| 14 | } from "@executor/platform-sdk/schema"; | ||
| 16 | import * as Effect from "effect/Effect"; | 15 | import * as Effect from "effect/Effect"; |
| 17 | import * as Layer from "effect/Layer"; | 16 | import * as Layer from "effect/Layer"; |
| 18 | import * as Option from "effect/Option"; | 17 | import * as Option from "effect/Option"; |
| 19 | 18 | ||
| 19 | import { | ||
| 20 | LocalInstanceConfigService, | ||
| 21 | SecretMaterialDeleterService, | ||
| 22 | SecretMaterialResolverService, | ||
| 23 | SecretMaterialStorerService, | ||
| 24 | SecretMaterialUpdaterService, | ||
| 25 | type ResolveSecretMaterial, | ||
| 26 | type ResolveInstanceConfig, | ||
| 27 | type SecretMaterialResolveContext, | ||
| 28 | type StoreSecretMaterial, | ||
| 29 | type DeleteSecretMaterial, | ||
| 30 | type UpdateSecretMaterial, | ||
| 31 | } from "../../sdk/src/runtime/workspace/secret-material-providers"; | ||
| 32 | import { fromConfigSecretProviderId } from "../../sdk/src/runtime/workspace/config-secrets"; | ||
| 33 | import { getRuntimeLocalWorkspaceOption } from "../../sdk/src/runtime/workspace/runtime-context"; | ||
| 34 | import { ControlPlaneStore } from "../../sdk/src/runtime/store"; | ||
| 35 | import type { ControlPlaneStoreShape } from "../../sdk/src/runtime/store"; | ||
| 36 | import { runtimeEffectError } from "../../sdk/src/runtime/effect-errors"; | ||
| 20 | import { resolveConfigRelativePath } from "./config"; | 37 | import { resolveConfigRelativePath } from "./config"; |
| 21 | import { fromConfigSecretProviderId } from "./config-secrets"; | ||
| 22 | import { getRuntimeLocalWorkspaceOption } from "./runtime-context"; | ||
| 23 | import { ControlPlaneStore } from "../store"; | ||
| 24 | import type { ControlPlaneStoreShape } from "../store"; | ||
| 25 | import { runtimeEffectError } from "../effect-errors"; | ||
| 26 | 38 | ||
| 27 | export const ENV_SECRET_PROVIDER_ID = "env"; | 39 | export const ENV_SECRET_PROVIDER_ID = "env"; |
| 28 | export const PARAMS_SECRET_PROVIDER_ID = "params"; | 40 | export const PARAMS_SECRET_PROVIDER_ID = "params"; |
| @@ -36,54 +48,6 @@ export type SecretStoreProviderId = · 7 unmodified lines | |||
| 36 | | typeof KEYCHAIN_SECRET_PROVIDER_ID | 48 | | typeof KEYCHAIN_SECRET_PROVIDER_ID |
| 37 | | typeof LOCAL_SECRET_PROVIDER_ID; | 49 | | typeof LOCAL_SECRET_PROVIDER_ID; |
| 38 | 50 | ||
| 39 | export type SecretMaterialResolveContext = { | ||
| 40 | params?: Readonly<Record<string, string | undefined>>; | ||
| 41 | }; | ||
| 42 | |||
| 43 | export type ResolveSecretMaterial = (input: { | ||
| 44 | ref: SecretRef; | ||
| 45 | context?: SecretMaterialResolveContext; | ||
| 46 | }) => Effect.Effect<string, Error, never>; | ||
| 47 | |||
| 48 | export type StoreSecretMaterial = (input: { | ||
| 49 | purpose: SecretMaterialPurpose; | ||
| 50 | value: string; | ||
| 51 | name?: string | null; | ||
| 52 | }) => Effect.Effect<SecretRef, Error, never>; | ||
| 53 | |||
| 54 | export type DeleteSecretMaterial = ( | ||
| 55 | ref: SecretRef, | ||
| 56 | ) => Effect.Effect<boolean, Error, never>; | ||
| 57 | |||
| 58 | export type UpdateSecretMaterial = (input: { | ||
| 59 | ref: SecretRef; | ||
| 60 | name?: string | null; | ||
| 61 | value?: string; | ||
| 62 | }) => Effect.Effect<{ | ||
| 63 | id: string; | ||
| 64 | providerId: string; | ||
| 65 | name: string | null; | ||
| 66 | purpose: string; | ||
| 67 | createdAt: number; | ||
| 68 | updatedAt: number; | ||
| 69 | }, Error, never>; | ||
| 70 | |||
| 71 | export class SecretMaterialResolverService extends Context.Tag( | ||
| 72 | "#runtime/SecretMaterialResolverService", | ||
| 73 | )<SecretMaterialResolverService, ResolveSecretMaterial>() {} | ||
| 74 | |||
| 75 | export class SecretMaterialStorerService extends Context.Tag( | ||
| 76 | "#runtime/SecretMaterialStorerService", | ||
| 77 | )<SecretMaterialStorerService, StoreSecretMaterial>() {} | ||
| 78 | |||
| 79 | export class SecretMaterialDeleterService extends Context.Tag( | ||
| 80 | "#runtime/SecretMaterialDeleterService", | ||
| 81 | )<SecretMaterialDeleterService, DeleteSecretMaterial>() {} | ||
| 82 | |||
| 83 | export class SecretMaterialUpdaterService extends Context.Tag( | ||
| 84 | "#runtime/SecretMaterialUpdaterService", | ||
| 85 | )<SecretMaterialUpdaterService, UpdateSecretMaterial>() {} | ||
| 86 | |||
| 87 | type SecretMaterialProviderRuntime = { | 51 | type SecretMaterialProviderRuntime = { |
| 88 | rows: ControlPlaneStoreShape; | 52 | rows: ControlPlaneStoreShape; |
| 89 | env: NodeJS.ProcessEnv; | 53 | env: NodeJS.ProcessEnv; |
| @@ -1085,17 +1049,16 @@ export const createDefaultSecretMaterialResolver = (input: { · 995 unmodified lines | |||
| 1085 | 1049 | ||
| 1086 | export const createDefaultSecretMaterialStorer = (input: { | 1050 | export const createDefaultSecretMaterialStorer = (input: { |
| 1087 | rows: ControlPlaneStoreShape; | 1051 | rows: ControlPlaneStoreShape; |
| 1088 | storeProviderId?: SecretStoreProviderId; | ||
| 1089 | dangerouslyAllowEnvSecrets?: boolean; | 1052 | dangerouslyAllowEnvSecrets?: boolean; |
| 1090 | keychainServiceName?: string; | 1053 | keychainServiceName?: string; |
| 1091 | }): StoreSecretMaterial => { | 1054 | }): StoreSecretMaterial => { |
| 1092 | const providers = createSecretMaterialProviderRegistry(); | 1055 | const providers = createSecretMaterialProviderRegistry(); |
| 1093 | const runtime = createSecretMaterialProviderRuntime(input); | 1056 | const runtime = createSecretMaterialProviderRuntime(input); |
| 1094 | 1057 | ||
| 1095 | return ({ purpose, value, name }) => | 1058 | return ({ purpose, value, name, providerId }) => |
| 1096 | Effect.gen(function* () { | 1059 | Effect.gen(function* () { |
| 1097 | const defaultStoreProviderId = yield* resolveDefaultSecretStoreProviderId({ | 1060 | const defaultStoreProviderId = yield* resolveDefaultSecretStoreProviderId({ |
| 1098 | storeProviderId: input.storeProviderId, | 1061 | storeProviderId: parseSecretStoreProviderId(providerId) ?? undefined, |
| 1099 | env: runtime.env, | 1062 | env: runtime.env, |
| 1100 | }); | 1063 | }); |
| 1101 | const provider = yield* getSecretMaterialProvider({ | 1064 | const provider = yield* getSecretMaterialProvider({ |
| @@ -1182,7 +1145,7 @@ const resolveRuntimeSecretMaterialConfig = (input: { · 80 unmodified lines | |||
| 1182 | ?? runtimeLocalWorkspace?.loadedConfig.config | 1145 | ?? runtimeLocalWorkspace?.loadedConfig.config |
| 1183 | ?? null, | 1146 | ?? null, |
| 1184 | workspaceRoot: input.workspaceRoot | 1147 | workspaceRoot: input.workspaceRoot |
| 1185 | ?? runtimeLocalWorkspace?.context.workspaceRoot | 1148 | ?? runtimeLocalWorkspace?.workspace.workspaceRoot |
| 1186 | ?? null, | 1149 | ?? null, |
| 1187 | }; | 1150 | }; |
| 1188 | }); | 1151 | }); |
| @@ -1213,7 +1176,6 @@ export const SecretMaterialResolverLive = (input: { · 24 unmodified lines | |||
| 1213 | ); | 1176 | ); |
| 1214 | 1177 | ||
| 1215 | export const SecretMaterialStorerLive = (input: { | 1178 | export const SecretMaterialStorerLive = (input: { |
| 1216 | storeProviderId?: SecretStoreProviderId; | ||
| 1217 | dangerouslyAllowEnvSecrets?: boolean; | 1179 | dangerouslyAllowEnvSecrets?: boolean; |
| 1218 | keychainServiceName?: string; | 1180 | keychainServiceName?: string; |
| 1219 | } = {}) => | 1181 | } = {}) => |
| @@ -1224,7 +1186,6 @@ export const SecretMaterialStorerLive = (input: { · 4 unmodified lines | |||
| 1224 | 1186 | ||
| 1225 | return createDefaultSecretMaterialStorer({ | 1187 | return createDefaultSecretMaterialStorer({ |
| 1226 | rows, | 1188 | rows, |
| 1227 | storeProviderId: input.storeProviderId, | ||
| 1228 | dangerouslyAllowEnvSecrets: input.dangerouslyAllowEnvSecrets, | 1189 | dangerouslyAllowEnvSecrets: input.dangerouslyAllowEnvSecrets, |
| 1229 | keychainServiceName: input.keychainServiceName, | 1190 | keychainServiceName: input.keychainServiceName, |
| 1230 | }); | 1191 | }); |
| @@ -1267,7 +1228,6 @@ export const SecretMaterialUpdaterLive = (input: { · 36 unmodified lines | |||
| 1267 | 1228 | ||
| 1268 | export const SecretMaterialLive = (input: { | 1229 | export const SecretMaterialLive = (input: { |
| 1269 | resolveSecretMaterial?: ResolveSecretMaterial; | 1230 | resolveSecretMaterial?: ResolveSecretMaterial; |
| 1270 | storeProviderId?: SecretStoreProviderId; | ||
| 1271 | dangerouslyAllowEnvSecrets?: boolean; | 1231 | dangerouslyAllowEnvSecrets?: boolean; |
| 1272 | keychainServiceName?: string; | 1232 | keychainServiceName?: string; |
| 1273 | localConfig?: LocalExecutorConfig | null; | 1233 | localConfig?: LocalExecutorConfig | null; |
| @@ -1279,3 +1239,46 @@ export const SecretMaterialLive = (input: { · 5 unmodified lines | |||
| 1279 | SecretMaterialDeleterLive(input), | 1239 | SecretMaterialDeleterLive(input), |
| 1280 | SecretMaterialUpdaterLive(input), | 1240 | SecretMaterialUpdaterLive(input), |
| 1281 | ); | 1241 | ); |
| 1242 | |||
| 1243 | export const createLocalInstanceConfigResolver = (): ResolveInstanceConfig => () => { | ||
| 1244 | const explicitDefaultStoreProvider = parseSecretStoreProviderId( | ||
| 1245 | process.env[SECRET_STORE_PROVIDER_ENV], | ||
| 1246 | ); | ||
| 1247 | const providers = [ | ||
| 1248 | { | ||
| 1249 | id: LOCAL_SECRET_PROVIDER_ID, | ||
| 1250 | name: "Local store", | ||
| 1251 | canStore: true, | ||
| 1252 | }, | ||
| 1253 | ]; | ||
| 1254 | |||
| 1255 | if (process.platform === "darwin" || process.platform === "linux") { | ||
| 1256 | providers.push({ | ||
| 1257 | id: KEYCHAIN_SECRET_PROVIDER_ID, | ||
| 1258 | name: | ||
| 1259 | process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring", | ||
| 1260 | canStore: | ||
| 1261 | process.platform === "darwin" || | ||
| 1262 | explicitDefaultStoreProvider === KEYCHAIN_SECRET_PROVIDER_ID, | ||
| 1263 | }); | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | providers.push({ | ||
| 1267 | id: ENV_SECRET_PROVIDER_ID, | ||
| 1268 | name: "Environment variable", | ||
| 1269 | canStore: false, | ||
| 1270 | }); | ||
| 1271 | |||
| 1272 | return resolveDefaultSecretStoreProviderId({ | ||
| 1273 | storeProviderId: explicitDefaultStoreProvider ?? undefined, | ||
| 1274 | }).pipe( | ||
| 1275 | Effect.map((resolvedDefaultStoreProvider) => ({ | ||
| 1276 | platform: process.platform, | ||
| 1277 | secretProviders: providers, | ||
| 1278 | defaultSecretStoreProvider: resolvedDefaultStoreProvider, | ||
| 1279 | })), | ||
| 1280 | ); | ||
| 1281 | }; | ||
| 1282 | |||
| 1283 | export const LocalInstanceConfigLive = () => | ||
| 1284 | Layer.succeed(LocalInstanceConfigService, createLocalInstanceConfigResolver()); | ||
| @@ -5,7 +5,11 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines | |||
| 5 | import { describe, expect, it } from "@effect/vitest"; | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | import * as Effect from "effect/Effect"; | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | ||
| 8 | import { SourceIdSchema, WorkspaceIdSchema, type Source } from "#schema"; | 8 | import { |
| 9 | SourceIdSchema, | ||
| 10 | WorkspaceIdSchema, | ||
| 11 | type Source, | ||
| 12 | } from "@executor/platform-sdk/schema"; | ||
| 9 | 13 | ||
| 10 | import { | 14 | import { |
| 11 | buildLocalSourceArtifact, | 15 | buildLocalSourceArtifact, |
| @@ -76,7 +80,7 @@ const makeArtifact = () => { · 64 unmodified lines | |||
| 76 | documents: [ | 80 | documents: [ |
| 77 | { | 81 | { |
| 78 | documentKind: "openapi", | 82 | documentKind: "openapi", |
| 79 | documentKey: source.binding.specUrl, | 83 | documentKey: String(source.binding.specUrl), |
| 80 | contentText: '{"openapi":"3.1.0"}', | 84 | contentText: '{"openapi":"3.1.0"}', |
| 81 | fetchedAt: 1, | 85 | fetchedAt: 1, |
| 82 | }, | 86 | }, |
| @@ -0,0 +1,172 @@ | |||
| 1 | import { join } from "node:path"; | ||
| 2 | import { FileSystem } from "@effect/platform"; | ||
| 3 | |||
| 4 | import type { NativeBlob } from "@executor/ir/model"; | ||
| 5 | import * as Effect from "effect/Effect"; | ||
| 6 | import type { Source } from "@executor/platform-sdk/schema"; | ||
| 7 | import type { SourceCatalogSyncResult } from "@executor/source-core"; | ||
| 8 | import { | ||
| 9 | buildLocalSourceArtifact, | ||
| 10 | decodeStoredLocalSourceArtifact, | ||
| 11 | hydrateLocalSourceArtifactDocuments, | ||
| 12 | splitLocalSourceArtifactDocuments, | ||
| 13 | type LocalSourceArtifact, | ||
| 14 | } from "../../sdk/src/runtime/source-artifacts"; | ||
| 15 | import type { ResolvedLocalWorkspaceContext } from "./config"; | ||
| 16 | import { | ||
| 17 | LocalFileSystemError, | ||
| 18 | unknownLocalErrorDetails, | ||
| 19 | } from "./errors"; | ||
| 20 | |||
| 21 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | ||
| 22 | new LocalFileSystemError({ | ||
| 23 | message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`, | ||
| 24 | action, | ||
| 25 | path, | ||
| 26 | details: unknownLocalErrorDetails(cause), | ||
| 27 | }); | ||
| 28 | |||
| 29 | const localSourceArtifactPath = (input: { | ||
| 30 | context: ResolvedLocalWorkspaceContext; | ||
| 31 | sourceId: string; | ||
| 32 | }): string => | ||
| 33 | join(input.context.artifactsDirectory, "sources", `${input.sourceId}.json`); | ||
| 34 | |||
| 35 | const localSourceDocumentDirectory = (input: { | ||
| 36 | context: ResolvedLocalWorkspaceContext; | ||
| 37 | sourceId: string; | ||
| 38 | }): string => | ||
| 39 | join(input.context.artifactsDirectory, "sources", input.sourceId, "documents"); | ||
| 40 | |||
| 41 | const localSourceDocumentPath = (input: { | ||
| 42 | context: ResolvedLocalWorkspaceContext; | ||
| 43 | sourceId: string; | ||
| 44 | documentId: string; | ||
| 45 | }): string => | ||
| 46 | join(localSourceDocumentDirectory(input), `${input.documentId}.txt`); | ||
| 47 | |||
| 48 | export { buildLocalSourceArtifact }; | ||
| 49 | export type { LocalSourceArtifact }; | ||
| 50 | |||
| 51 | export const readLocalSourceArtifact = (input: { | ||
| 52 | context: ResolvedLocalWorkspaceContext; | ||
| 53 | sourceId: string; | ||
| 54 | }): Effect.Effect< | ||
| 55 | LocalSourceArtifact | null, | ||
| 56 | LocalFileSystemError, | ||
| 57 | FileSystem.FileSystem | ||
| 58 | > => | ||
| 59 | Effect.gen(function* () { | ||
| 60 | const fs = yield* FileSystem.FileSystem; | ||
| 61 | const path = localSourceArtifactPath(input); | ||
| 62 | const exists = yield* fs.exists(path).pipe( | ||
| 63 | Effect.mapError(mapFileSystemError(path, "check source artifact path")), | ||
| 64 | ); | ||
| 65 | if (!exists) { | ||
| 66 | return null; | ||
| 67 | } | ||
| 68 | |||
| 69 | const content = yield* fs.readFileString(path, "utf8").pipe( | ||
| 70 | Effect.mapError(mapFileSystemError(path, "read source artifact")), | ||
| 71 | ); | ||
| 72 | const decodedSnapshotArtifact = decodeStoredLocalSourceArtifact(content); | ||
| 73 | if (decodedSnapshotArtifact === null) { | ||
| 74 | return null; | ||
| 75 | } | ||
| 76 | |||
| 77 | const rawDocuments: Record<string, NativeBlob> = {}; | ||
| 78 | for (const documentId of Object.keys(decodedSnapshotArtifact.snapshot.catalog.documents)) { | ||
| 79 | const sourceDocumentPath = localSourceDocumentPath({ | ||
| 80 | context: input.context, | ||
| 81 | sourceId: input.sourceId, | ||
| 82 | documentId, | ||
| 83 | }); | ||
| 84 | const sourceDocumentExists = yield* fs.exists(sourceDocumentPath).pipe( | ||
| 85 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "check source document path")), | ||
| 86 | ); | ||
| 87 | if (!sourceDocumentExists) { | ||
| 88 | continue; | ||
| 89 | } | ||
| 90 | |||
| 91 | const sourceContent = yield* fs.readFileString(sourceDocumentPath, "utf8").pipe( | ||
| 92 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "read source document")), | ||
| 93 | ); | ||
| 94 | rawDocuments[documentId] = { | ||
| 95 | sourceKind: decodedSnapshotArtifact.snapshot.import.sourceKind, | ||
| 96 | kind: "source_document", | ||
| 97 | value: sourceContent, | ||
| 98 | } satisfies NativeBlob; | ||
| 99 | } | ||
| 100 | |||
| 101 | return Object.keys(rawDocuments).length > 0 | ||
| 102 | ? hydrateLocalSourceArtifactDocuments({ | ||
| 103 | artifact: decodedSnapshotArtifact, | ||
| 104 | rawDocuments, | ||
| 105 | }) | ||
| 106 | : decodedSnapshotArtifact; | ||
| 107 | }); | ||
| 108 | |||
| 109 | export const writeLocalSourceArtifact = (input: { | ||
| 110 | context: ResolvedLocalWorkspaceContext; | ||
| 111 | sourceId: string; | ||
| 112 | artifact: LocalSourceArtifact; | ||
| 113 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 114 | Effect.gen(function* () { | ||
| 115 | const fs = yield* FileSystem.FileSystem; | ||
| 116 | const directory = join(input.context.artifactsDirectory, "sources"); | ||
| 117 | const path = localSourceArtifactPath(input); | ||
| 118 | const sourceDocumentDirectory = localSourceDocumentDirectory(input); | ||
| 119 | const split = splitLocalSourceArtifactDocuments(input.artifact); | ||
| 120 | |||
| 121 | yield* fs.makeDirectory(directory, { recursive: true }).pipe( | ||
| 122 | Effect.mapError(mapFileSystemError(directory, "create source artifact directory")), | ||
| 123 | ); | ||
| 124 | yield* fs.remove(sourceDocumentDirectory, { recursive: true, force: true }).pipe( | ||
| 125 | Effect.mapError( | ||
| 126 | mapFileSystemError(sourceDocumentDirectory, "remove source document directory"), | ||
| 127 | ), | ||
| 128 | ); | ||
| 129 | if (split.rawDocuments.length > 0) { | ||
| 130 | yield* fs.makeDirectory(sourceDocumentDirectory, { recursive: true }).pipe( | ||
| 131 | Effect.mapError( | ||
| 132 | mapFileSystemError(sourceDocumentDirectory, "create source document directory"), | ||
| 133 | ), | ||
| 134 | ); | ||
| 135 | for (const rawDocument of split.rawDocuments) { | ||
| 136 | const sourceDocumentPath = localSourceDocumentPath({ | ||
| 137 | context: input.context, | ||
| 138 | sourceId: input.sourceId, | ||
| 139 | documentId: rawDocument.documentId, | ||
| 140 | }); | ||
| 141 | yield* fs.writeFileString(sourceDocumentPath, rawDocument.content).pipe( | ||
| 142 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "write source document")), | ||
| 143 | ); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | yield* fs.writeFileString(path, `${JSON.stringify(split.artifact)}\n`).pipe( | ||
| 147 | Effect.mapError(mapFileSystemError(path, "write source artifact")), | ||
| 148 | ); | ||
| 149 | }); | ||
| 150 | |||
| 151 | export const removeLocalSourceArtifact = (input: { | ||
| 152 | context: ResolvedLocalWorkspaceContext; | ||
| 153 | sourceId: string; | ||
| 154 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 155 | Effect.gen(function* () { | ||
| 156 | const fs = yield* FileSystem.FileSystem; | ||
| 157 | const path = localSourceArtifactPath(input); | ||
| 158 | const exists = yield* fs.exists(path).pipe( | ||
| 159 | Effect.mapError(mapFileSystemError(path, "check source artifact path")), | ||
| 160 | ); | ||
| 161 | if (exists) { | ||
| 162 | yield* fs.remove(path).pipe( | ||
| 163 | Effect.mapError(mapFileSystemError(path, "remove source artifact")), | ||
| 164 | ); | ||
| 165 | } | ||
| 166 | const sourceDocumentDirectory = localSourceDocumentDirectory(input); | ||
| 167 | yield* fs.remove(sourceDocumentDirectory, { recursive: true, force: true }).pipe( | ||
| 168 | Effect.mapError( | ||
| 169 | mapFileSystemError(sourceDocumentDirectory, "remove source document directory"), | ||
| 170 | ), | ||
| 171 | ); | ||
| 172 | }); | ||
| @@ -5,7 +5,11 @@ import { NodeFileSystem } from "@effect/platform-node"; · 4 unmodified lines | |||
| 5 | import { describe, expect, it } from "@effect/vitest"; | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | import * as Effect from "effect/Effect"; | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | ||
| 8 | import { SourceIdSchema, WorkspaceIdSchema, type Source } from "#schema"; | 8 | import { |
| 9 | SourceIdSchema, | ||
| 10 | WorkspaceIdSchema, | ||
| 11 | type Source, | ||
| 12 | } from "@executor/platform-sdk/schema"; | ||
| 9 | 13 | ||
| 10 | import { | 14 | import { |
| 11 | createCatalogSnapshotV1, | 15 | createCatalogSnapshotV1, |
| @@ -21,7 +25,7 @@ import { · 9 unmodified lines | |||
| 21 | ShapeSymbolIdSchema, | 25 | ShapeSymbolIdSchema, |
| 22 | } from "@executor/ir/ids"; | 26 | } from "@executor/ir/ids"; |
| 23 | import type { CatalogV1, ProvenanceRef } from "@executor/ir/model"; | 27 | import type { CatalogV1, ProvenanceRef } from "@executor/ir/model"; |
| 24 | import { syncWorkspaceSourceTypeDeclarationsNode } from "./type-declarations"; | 28 | import { syncWorkspaceSourceTypeDeclarationsNode } from "./source-type-declarations"; |
| 25 | 29 | ||
| 26 | const put = <K extends string, V>(record: Record<K, V>, key: K, value: V) => { | 30 | const put = <K extends string, V>(record: Record<K, V>, key: K, value: V) => { |
| 27 | record[key] = value; | 31 | record[key] = value; |
| @@ -0,0 +1,427 @@ | |||
| 1 | import { join } from "node:path"; | ||
| 2 | import { FileSystem } from "@effect/platform"; | ||
| 3 | import { NodeFileSystem } from "@effect/platform-node"; | ||
| 4 | import type { Source } from "@executor/platform-sdk/schema"; | ||
| 5 | import * as Cause from "effect/Cause"; | ||
| 6 | import * as Effect from "effect/Effect"; | ||
| 7 | import * as Fiber from "effect/Fiber"; | ||
| 8 | import type { CatalogSnapshotV1 } from "@executor/ir/model"; | ||
| 9 | |||
| 10 | import { projectCatalogForAgentSdk } from "@executor/ir/catalog"; | ||
| 11 | import { | ||
| 12 | createCatalogTypeProjector, | ||
| 13 | formatPropertyKey, | ||
| 14 | joinTypeNameSegments, | ||
| 15 | projectedCatalogTypeRoots, | ||
| 16 | shapeAllowsOmittedArgs, | ||
| 17 | } from "../../sdk/src/runtime/catalog/catalog-typescript"; | ||
| 18 | import type { ResolvedLocalWorkspaceContext } from "./config"; | ||
| 19 | import { | ||
| 20 | LocalFileSystemError, | ||
| 21 | unknownLocalErrorDetails, | ||
| 22 | } from "./errors"; | ||
| 23 | |||
| 24 | type SourceDeclarationEntry = { | ||
| 25 | source: Source; | ||
| 26 | snapshot: CatalogSnapshotV1; | ||
| 27 | }; | ||
| 28 | |||
| 29 | type ToolMethodNode = { | ||
| 30 | readonly segments: readonly string[]; | ||
| 31 | readonly inputType: string; | ||
| 32 | readonly outputType: string; | ||
| 33 | readonly argsOptional: boolean; | ||
| 34 | }; | ||
| 35 | |||
| 36 | type SourceDeclarationModel = { | ||
| 37 | readonly methods: readonly ToolMethodNode[]; | ||
| 38 | readonly supportingTypes: readonly string[]; | ||
| 39 | }; | ||
| 40 | |||
| 41 | type ToolTreeNode = { | ||
| 42 | method: ToolMethodNode | null; | ||
| 43 | children: Map<string, ToolTreeNode>; | ||
| 44 | }; | ||
| 45 | |||
| 46 | type SourceDeclarationStub = { | ||
| 47 | sourceId: string; | ||
| 48 | }; | ||
| 49 | |||
| 50 | const GENERATED_TYPES_DIRECTORY = "types"; | ||
| 51 | const GENERATED_SOURCE_TYPES_DIRECTORY = "sources"; | ||
| 52 | |||
| 53 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | ||
| 54 | new LocalFileSystemError({ | ||
| 55 | message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`, | ||
| 56 | action, | ||
| 57 | path, | ||
| 58 | details: unknownLocalErrorDetails(cause), | ||
| 59 | }); | ||
| 60 | |||
| 61 | const declarationDirectory = (context: ResolvedLocalWorkspaceContext): string => | ||
| 62 | join(context.configDirectory, GENERATED_TYPES_DIRECTORY); | ||
| 63 | |||
| 64 | const sourceDeclarationDirectory = (context: ResolvedLocalWorkspaceContext): string => | ||
| 65 | join(declarationDirectory(context), GENERATED_SOURCE_TYPES_DIRECTORY); | ||
| 66 | |||
| 67 | const sourceDeclarationFileName = (sourceId: string): string => `${sourceId}.d.ts`; | ||
| 68 | |||
| 69 | const sourceDeclarationPath = (context: ResolvedLocalWorkspaceContext, sourceId: string): string => | ||
| 70 | join(sourceDeclarationDirectory(context), sourceDeclarationFileName(sourceId)); | ||
| 71 | |||
| 72 | const aggregateDeclarationPath = (context: ResolvedLocalWorkspaceContext): string => | ||
| 73 | join(declarationDirectory(context), "index.d.ts"); | ||
| 74 | |||
| 75 | const sourceTypeInterfaceName = (sourceId: string): string => | ||
| 76 | `SourceTools_${sourceId.replace(/[^A-Za-z0-9_$]+/g, "_")}`; | ||
| 77 | |||
| 78 | const methodSignature = (method: ToolMethodNode): string => | ||
| 79 | `(${method.argsOptional ? "args?:" : "args:"} ${method.inputType}) => Promise<${method.outputType}>`; | ||
| 80 | |||
| 81 | const createToolTreeNode = (): ToolTreeNode => ({ | ||
| 82 | method: null, | ||
| 83 | children: new Map(), | ||
| 84 | }); | ||
| 85 | |||
| 86 | const objectTypeLiteral = ( | ||
| 87 | lines: readonly string[], | ||
| 88 | indent: string, | ||
| 89 | ): string => { | ||
| 90 | if (lines.length === 0) { | ||
| 91 | return "{}"; | ||
| 92 | } | ||
| 93 | |||
| 94 | return [ | ||
| 95 | "{", | ||
| 96 | ...lines.map((line) => `${indent}${line}`), | ||
| 97 | `${indent.slice(0, -2)}}`, | ||
| 98 | ].join("\n"); | ||
| 99 | }; | ||
| 100 | |||
| 101 | const renderToolTreeType = (node: ToolTreeNode, indentLevel: number): string => { | ||
| 102 | const indent = " ".repeat(indentLevel + 1); | ||
| 103 | const childLines = [...node.children.entries()] | ||
| 104 | .sort(([left], [right]) => left.localeCompare(right)) | ||
| 105 | .map(([segment, child]) => | ||
| 106 | `${formatPropertyKey(segment)}: ${renderToolTreeType(child, indentLevel + 1)};` | ||
| 107 | ); | ||
| 108 | |||
| 109 | const objectType = objectTypeLiteral(childLines, indent); | ||
| 110 | if (node.method === null) { | ||
| 111 | return objectType; | ||
| 112 | } | ||
| 113 | |||
| 114 | const callable = methodSignature(node.method); | ||
| 115 | return node.children.size === 0 | ||
| 116 | ? callable | ||
| 117 | : `(${callable}) & ${objectType}`; | ||
| 118 | }; | ||
| 119 | |||
| 120 | const buildToolTree = (methods: readonly ToolMethodNode[]): ToolTreeNode => { | ||
| 121 | const root = createToolTreeNode(); | ||
| 122 | |||
| 123 | for (const method of methods) { | ||
| 124 | let current = root; | ||
| 125 | for (const segment of method.segments) { | ||
| 126 | const existing = current.children.get(segment); | ||
| 127 | if (existing) { | ||
| 128 | current = existing; | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | const next = createToolTreeNode(); | ||
| 133 | current.children.set(segment, next); | ||
| 134 | current = next; | ||
| 135 | } | ||
| 136 | |||
| 137 | current.method = method; | ||
| 138 | } | ||
| 139 | |||
| 140 | return root; | ||
| 141 | }; | ||
| 142 | |||
| 143 | const buildSourceDeclarationModel = (snapshot: CatalogSnapshotV1): SourceDeclarationModel => { | ||
| 144 | const projected = projectCatalogForAgentSdk({ | ||
| 145 | catalog: snapshot.catalog, | ||
| 146 | }); | ||
| 147 | const toolDescriptors = Object.values(projected.toolDescriptors) | ||
| 148 | .sort((left, right) => left.toolPath.join(".").localeCompare(right.toolPath.join("."))); | ||
| 149 | const renderer = createCatalogTypeProjector({ | ||
| 150 | catalog: projected.catalog, | ||
| 151 | roots: projectedCatalogTypeRoots(projected), | ||
| 152 | }); | ||
| 153 | |||
| 154 | const methods = toolDescriptors | ||
| 155 | .map((descriptor) => ({ | ||
| 156 | segments: descriptor.toolPath, | ||
| 157 | inputType: renderer.renderDeclarationShape(descriptor.callShapeId, { | ||
| 158 | aliasHint: joinTypeNameSegments(...descriptor.toolPath, "call"), | ||
| 159 | }), | ||
| 160 | outputType: descriptor.resultShapeId | ||
| 161 | ? renderer.renderDeclarationShape(descriptor.resultShapeId, { | ||
| 162 | aliasHint: joinTypeNameSegments(...descriptor.toolPath, "result"), | ||
| 163 | }) | ||
| 164 | : "unknown", | ||
| 165 | argsOptional: shapeAllowsOmittedArgs(projected.catalog, descriptor.callShapeId), | ||
| 166 | }) satisfies ToolMethodNode); | ||
| 167 | |||
| 168 | return { | ||
| 169 | methods, | ||
| 170 | supportingTypes: renderer.supportingDeclarations(), | ||
| 171 | }; | ||
| 172 | }; | ||
| 173 | |||
| 174 | const sourceDeclarationText = (entry: SourceDeclarationEntry): string => { | ||
| 175 | const interfaceName = sourceTypeInterfaceName(entry.source.id); | ||
| 176 | const declarationModel = buildSourceDeclarationModel(entry.snapshot); | ||
| 177 | const tree = buildToolTree(declarationModel.methods); | ||
| 178 | const body = renderToolTreeType(tree, 0); | ||
| 179 | |||
| 180 | return [ | ||
| 181 | "// Generated by executor. Do not edit by hand.", | ||
| 182 | `// Source: ${entry.source.name} (${entry.source.id})`, | ||
| 183 | "", | ||
| 184 | ...declarationModel.supportingTypes, | ||
| 185 | ...(declarationModel.supportingTypes.length > 0 ? [""] : []), | ||
| 186 | `export interface ${interfaceName} ${body}`, | ||
| 187 | "", | ||
| 188 | `export declare const tools: ${interfaceName};`, | ||
| 189 | `export type ${interfaceName}Tools = ${interfaceName};`, | ||
| 190 | "export default tools;", | ||
| 191 | "", | ||
| 192 | ].join("\n"); | ||
| 193 | }; | ||
| 194 | |||
| 195 | const aggregateDeclarationText = (entries: readonly SourceDeclarationEntry[]): string => { | ||
| 196 | return aggregateDeclarationTextFromSourceIds( | ||
| 197 | entries.map((entry) => ({ sourceId: entry.source.id })), | ||
| 198 | ); | ||
| 199 | }; | ||
| 200 | |||
| 201 | const aggregateDeclarationTextFromSourceIds = (entries: readonly SourceDeclarationStub[]): string => { | ||
| 202 | const sorted = [...entries].sort((left, right) => left.sourceId.localeCompare(right.sourceId)); | ||
| 203 | const imports = sorted.map((entry) => | ||
| 204 | `import type { ${sourceTypeInterfaceName(entry.sourceId)} } from "../sources/${entry.sourceId}";` | ||
| 205 | ); | ||
| 206 | const intersections = sorted.map((entry) => sourceTypeInterfaceName(entry.sourceId)); | ||
| 207 | const executorToolsType = intersections.length > 0 | ||
| 208 | ? intersections.join(" & ") | ||
| 209 | : "{}"; | ||
| 210 | |||
| 211 | return [ | ||
| 212 | "// Generated by executor. Do not edit by hand.", | ||
| 213 | ...imports, | ||
| 214 | ...(imports.length > 0 ? [""] : []), | ||
| 215 | `export type ExecutorSourceTools = ${executorToolsType};`, | ||
| 216 | "", | ||
| 217 | "declare global {", | ||
| 218 | " const tools: ExecutorSourceTools;", | ||
| 219 | "}", | ||
| 220 | "", | ||
| 221 | "export declare const tools: ExecutorSourceTools;", | ||
| 222 | "export default tools;", | ||
| 223 | "", | ||
| 224 | ].join("\n"); | ||
| 225 | }; | ||
| 226 | |||
| 227 | export const syncWorkspaceSourceTypeDeclarations = (input: { | ||
| 228 | context: ResolvedLocalWorkspaceContext; | ||
| 229 | entries: readonly SourceDeclarationEntry[]; | ||
| 230 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 231 | Effect.gen(function* () { | ||
| 232 | const fs = yield* FileSystem.FileSystem; | ||
| 233 | const declarationsDir = declarationDirectory(input.context); | ||
| 234 | const perSourceDir = sourceDeclarationDirectory(input.context); | ||
| 235 | const activeEntries = input.entries | ||
| 236 | .filter((entry) => entry.source.enabled && entry.source.status === "connected") | ||
| 237 | .sort((left, right) => left.source.id.localeCompare(right.source.id)); | ||
| 238 | |||
| 239 | yield* fs.makeDirectory(declarationsDir, { recursive: true }).pipe( | ||
| 240 | Effect.mapError(mapFileSystemError(declarationsDir, "create declaration directory")), | ||
| 241 | ); | ||
| 242 | yield* fs.makeDirectory(perSourceDir, { recursive: true }).pipe( | ||
| 243 | Effect.mapError(mapFileSystemError(perSourceDir, "create source declaration directory")), | ||
| 244 | ); | ||
| 245 | |||
| 246 | const expectedFiles = new Set( | ||
| 247 | activeEntries.map((entry) => sourceDeclarationFileName(entry.source.id)), | ||
| 248 | ); | ||
| 249 | const existingFiles = yield* fs.readDirectory(perSourceDir).pipe( | ||
| 250 | Effect.mapError(mapFileSystemError(perSourceDir, "read source declaration directory")), | ||
| 251 | ); | ||
| 252 | |||
| 253 | for (const existingFile of existingFiles) { | ||
| 254 | if (expectedFiles.has(existingFile)) { | ||
| 255 | continue; | ||
| 256 | } | ||
| 257 | |||
| 258 | const stalePath = join(perSourceDir, existingFile); | ||
| 259 | yield* fs.remove(stalePath).pipe( | ||
| 260 | Effect.mapError(mapFileSystemError(stalePath, "remove stale source declaration")), | ||
| 261 | ); | ||
| 262 | } | ||
| 263 | |||
| 264 | for (const entry of activeEntries) { | ||
| 265 | const filePath = sourceDeclarationPath(input.context, entry.source.id); | ||
| 266 | yield* fs.writeFileString(filePath, sourceDeclarationText(entry)).pipe( | ||
| 267 | Effect.mapError(mapFileSystemError(filePath, "write source declaration")), | ||
| 268 | ); | ||
| 269 | } | ||
| 270 | |||
| 271 | const aggregatePath = aggregateDeclarationPath(input.context); | ||
| 272 | yield* fs.writeFileString( | ||
| 273 | aggregatePath, | ||
| 274 | aggregateDeclarationText(activeEntries), | ||
| 275 | ).pipe( | ||
| 276 | Effect.mapError(mapFileSystemError(aggregatePath, "write aggregate declaration")), | ||
| 277 | ); | ||
| 278 | }); | ||
| 279 | |||
| 280 | export const syncSourceTypeDeclaration = (input: { | ||
| 281 | context: ResolvedLocalWorkspaceContext; | ||
| 282 | source: Source; | ||
| 283 | snapshot: CatalogSnapshotV1 | null; | ||
| 284 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 285 | Effect.gen(function* () { | ||
| 286 | const fs = yield* FileSystem.FileSystem; | ||
| 287 | const declarationsDir = declarationDirectory(input.context); | ||
| 288 | const perSourceDir = sourceDeclarationDirectory(input.context); | ||
| 289 | |||
| 290 | yield* fs.makeDirectory(declarationsDir, { recursive: true }).pipe( | ||
| 291 | Effect.mapError(mapFileSystemError(declarationsDir, "create declaration directory")), | ||
| 292 | ); | ||
| 293 | yield* fs.makeDirectory(perSourceDir, { recursive: true }).pipe( | ||
| 294 | Effect.mapError(mapFileSystemError(perSourceDir, "create source declaration directory")), | ||
| 295 | ); | ||
| 296 | |||
| 297 | const filePath = sourceDeclarationPath(input.context, input.source.id); | ||
| 298 | const shouldWrite = input.source.enabled | ||
| 299 | && input.source.status === "connected" | ||
| 300 | && input.snapshot !== null; | ||
| 301 | |||
| 302 | if (shouldWrite) { | ||
| 303 | const snapshot = input.snapshot; | ||
| 304 | if (snapshot === null) { | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | yield* fs.writeFileString( | ||
| 308 | filePath, | ||
| 309 | sourceDeclarationText({ | ||
| 310 | source: input.source, | ||
| 311 | snapshot, | ||
| 312 | }), | ||
| 313 | ).pipe( | ||
| 314 | Effect.mapError(mapFileSystemError(filePath, "write source declaration")), | ||
| 315 | ); | ||
| 316 | } else { | ||
| 317 | const exists = yield* fs.exists(filePath).pipe( | ||
| 318 | Effect.mapError(mapFileSystemError(filePath, "check source declaration path")), | ||
| 319 | ); | ||
| 320 | if (exists) { | ||
| 321 | yield* fs.remove(filePath).pipe( | ||
| 322 | Effect.mapError(mapFileSystemError(filePath, "remove source declaration")), | ||
| 323 | ); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | const sourceIds = (yield* fs.readDirectory(perSourceDir).pipe( | ||
| 328 | Effect.mapError(mapFileSystemError(perSourceDir, "read source declaration directory")), | ||
| 329 | )) | ||
| 330 | .filter((fileName) => fileName.endsWith(".d.ts")) | ||
| 331 | .map((fileName) => ({ sourceId: fileName.slice(0, -".d.ts".length) })); | ||
| 332 | |||
| 333 | const aggregatePath = aggregateDeclarationPath(input.context); | ||
| 334 | yield* fs.writeFileString( | ||
| 335 | aggregatePath, | ||
| 336 | aggregateDeclarationTextFromSourceIds(sourceIds), | ||
| 337 | ).pipe( | ||
| 338 | Effect.mapError(mapFileSystemError(aggregatePath, "write aggregate declaration")), | ||
| 339 | ); | ||
| 340 | }); | ||
| 341 | |||
| 342 | export const syncWorkspaceSourceTypeDeclarationsNode = (input: { | ||
| 343 | context: ResolvedLocalWorkspaceContext; | ||
| 344 | entries: readonly SourceDeclarationEntry[]; | ||
| 345 | }): Effect.Effect<void, LocalFileSystemError, never> => | ||
| 346 | syncWorkspaceSourceTypeDeclarations(input).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 347 | |||
| 348 | export const syncSourceTypeDeclarationNode = (input: { | ||
| 349 | context: ResolvedLocalWorkspaceContext; | ||
| 350 | source: Source; | ||
| 351 | snapshot: CatalogSnapshotV1 | null; | ||
| 352 | }): Effect.Effect<void, LocalFileSystemError, never> => | ||
| 353 | syncSourceTypeDeclaration(input).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 354 | |||
| 355 | const logBackgroundDeclarationError = (label: string, cause: unknown): void => { | ||
| 356 | const message = Cause.isCause(cause) | ||
| 357 | ? Cause.pretty(cause) | ||
| 358 | : cause instanceof Error | ||
| 359 | ? cause.message | ||
| 360 | : String(cause); | ||
| 361 | console.warn(`[source-types] ${label} failed: ${message}`); | ||
| 362 | }; | ||
| 363 | |||
| 364 | const BACKGROUND_DECLARATION_REFRESH_DELAY = "1500 millis"; | ||
| 365 | const workspaceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>(); | ||
| 366 | const sourceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>(); | ||
| 367 | |||
| 368 | export const refreshWorkspaceSourceTypeDeclarationsInBackground = (input: { | ||
| 369 | context: ResolvedLocalWorkspaceContext; | ||
| 370 | entries: readonly SourceDeclarationEntry[]; | ||
| 371 | }): void => { | ||
| 372 | const key = input.context.configDirectory; | ||
| 373 | const existingFiber = workspaceRefreshFibers.get(key); | ||
| 374 | if (existingFiber) { | ||
| 375 | Effect.runFork(Fiber.interruptFork(existingFiber)); | ||
| 376 | } | ||
| 377 | |||
| 378 | const fiber = Effect.runFork( | ||
| 379 | Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe( | ||
| 380 | Effect.zipRight( | ||
| 381 | syncWorkspaceSourceTypeDeclarationsNode(input).pipe( | ||
| 382 | Effect.catchAllCause((cause) => | ||
| 383 | Effect.sync(() => { | ||
| 384 | logBackgroundDeclarationError("workspace declaration refresh", cause); | ||
| 385 | }) | ||
| 386 | ), | ||
| 387 | ), | ||
| 388 | ), | ||
| 389 | ), | ||
| 390 | ); | ||
| 391 | |||
| 392 | workspaceRefreshFibers.set(key, fiber); | ||
| 393 | fiber.addObserver(() => { | ||
| 394 | workspaceRefreshFibers.delete(key); | ||
| 395 | }); | ||
| 396 | }; | ||
| 397 | |||
| 398 | export const refreshSourceTypeDeclarationInBackground = (input: { | ||
| 399 | context: ResolvedLocalWorkspaceContext; | ||
| 400 | source: Source; | ||
| 401 | snapshot: CatalogSnapshotV1 | null; | ||
| 402 | }): void => { | ||
| 403 | const key = `${input.context.configDirectory}:${input.source.id}`; | ||
| 404 | const existingFiber = sourceRefreshFibers.get(key); | ||
| 405 | if (existingFiber) { | ||
| 406 | Effect.runFork(Fiber.interruptFork(existingFiber)); | ||
| 407 | } | ||
| 408 | |||
| 409 | const fiber = Effect.runFork( | ||
| 410 | Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe( | ||
| 411 | Effect.zipRight( | ||
| 412 | syncSourceTypeDeclarationNode(input).pipe( | ||
| 413 | Effect.catchAllCause((cause) => | ||
| 414 | Effect.sync(() => { | ||
| 415 | logBackgroundDeclarationError(`source ${input.source.id} declaration refresh`, cause); | ||
| 416 | }) | ||
| 417 | ), | ||
| 418 | ), | ||
| 419 | ), | ||
| 420 | ), | ||
| 421 | ); | ||
| 422 | |||
| 423 | sourceRefreshFibers.set(key, fiber); | ||
| 424 | fiber.addObserver(() => { | ||
| 425 | sourceRefreshFibers.delete(key); | ||
| 426 | }); | ||
| 427 | }; | ||
| @@ -15,11 +15,10 @@ import { · 14 unmodified lines | |||
| 15 | type ToolMetadata, | 15 | type ToolMetadata, |
| 16 | type ToolPath, | 16 | type ToolPath, |
| 17 | } from "@executor/codemode-core"; | 17 | } from "@executor/codemode-core"; |
| 18 | import * as Context from "effect/Context"; | ||
| 19 | import * as Effect from "effect/Effect"; | 18 | import * as Effect from "effect/Effect"; |
| 20 | import * as Layer from "effect/Layer"; | ||
| 21 | import * as ts from "typescript"; | 19 | import * as ts from "typescript"; |
| 22 | 20 | ||
| 21 | import type { LocalToolRuntime } from "../../sdk/src/runtime/local-tool-runtime"; | ||
| 23 | import type { ResolvedLocalWorkspaceContext } from "./config"; | 22 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| 24 | import { | 23 | import { |
| 25 | LocalFileSystemError, | 24 | LocalFileSystemError, |
| @@ -79,13 +78,6 @@ export const defineLocalTool = < · 53 unmodified lines | |||
| 79 | | LocalToolWithMetadata<TInputSchema, TOutputSchema>, | 78 | | LocalToolWithMetadata<TInputSchema, TOutputSchema>, |
| 80 | ): typeof input => input; | 79 | ): typeof input => input; |
| 81 | 80 | ||
| 82 | export type LocalToolRuntime = { | ||
| 83 | tools: ToolMap; | ||
| 84 | catalog: ToolCatalog; | ||
| 85 | toolInvoker: ToolInvoker; | ||
| 86 | toolPaths: Set<string>; | ||
| 87 | }; | ||
| 88 | |||
| 89 | const emptyLocalToolRuntime = (): LocalToolRuntime => { | 81 | const emptyLocalToolRuntime = (): LocalToolRuntime => { |
| 90 | const tools: ToolMap = {}; | 82 | const tools: ToolMap = {}; |
| 91 | return { | 83 | return { |
| @@ -651,35 +643,3 @@ export const loadLocalToolRuntime = ( · 559 unmodified lines | |||
| 651 | toolPaths: new Set(Object.keys(tools)), | 643 | toolPaths: new Set(Object.keys(tools)), |
| 652 | } satisfies LocalToolRuntime; | 644 | } satisfies LocalToolRuntime; |
| 653 | }); | 645 | }); |
| 654 | |||
| 655 | export type LocalToolRuntimeLoaderShape = { | ||
| 656 | load: ( | ||
| 657 | context: ResolvedLocalWorkspaceContext, | ||
| 658 | ) => Effect.Effect< | ||
| 659 | LocalToolRuntime, | ||
| 660 | | LocalFileSystemError | ||
| 661 | | LocalToolDefinitionError | ||
| 662 | | LocalToolImportError | ||
| 663 | | LocalToolPathConflictError | ||
| 664 | | LocalToolTranspileError, | ||
| 665 | never | ||
| 666 | >; | ||
| 667 | }; | ||
| 668 | |||
| 669 | export class LocalToolRuntimeLoaderService extends Context.Tag( | ||
| 670 | "#runtime/LocalToolRuntimeLoaderService", | ||
| 671 | )<LocalToolRuntimeLoaderService, LocalToolRuntimeLoaderShape>() {} | ||
| 672 | |||
| 673 | export const LocalToolRuntimeLoaderLive = Layer.effect( | ||
| 674 | LocalToolRuntimeLoaderService, | ||
| 675 | Effect.gen(function* () { | ||
| 676 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 677 | |||
| 678 | return LocalToolRuntimeLoaderService.of({ | ||
| 679 | load: (context) => | ||
| 680 | loadLocalToolRuntime(context).pipe( | ||
| 681 | Effect.provideService(FileSystem.FileSystem, fileSystem), | ||
| 682 | ), | ||
| 683 | }); | ||
| 684 | }), | ||
| 685 | ); | ||
| @@ -1,14 +1,13 @@ | |||
| 1 | import { join } from "node:path"; | 1 | import { join } from "node:path"; |
| 2 | import { FileSystem } from "@effect/platform"; | 2 | import { FileSystem } from "@effect/platform"; |
| 3 | |||
| 4 | import { | ||
| 5 | PolicyIdSchema, | ||
| 6 | SourceStatusSchema, | ||
| 7 | TimestampMsSchema, | ||
| 8 | } from "#schema"; | ||
| 9 | import * as Effect from "effect/Effect"; | 3 | import * as Effect from "effect/Effect"; |
| 10 | import * as Schema from "effect/Schema"; | 4 | import * as Schema from "effect/Schema"; |
| 11 | 5 | ||
| 6 | import type { LocalWorkspaceState } from "../../sdk/src/runtime/workspace-state"; | ||
| 7 | import { | ||
| 8 | defaultLocalWorkspaceState, | ||
| 9 | LocalWorkspaceStateSchema, | ||
| 10 | } from "../../sdk/src/runtime/workspace-state"; | ||
| 12 | import type { ResolvedLocalWorkspaceContext } from "./config"; | 11 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| 13 | import { | 12 | import { |
| 14 | LocalFileSystemError, | 13 | LocalFileSystemError, |
| @@ -18,36 +17,6 @@ import { · 3 unmodified lines | |||
| 18 | 17 | ||
| 19 | const WORKSPACE_STATE_BASENAME = "workspace-state.json"; | 18 | const WORKSPACE_STATE_BASENAME = "workspace-state.json"; |
| 20 | 19 | ||
| 21 | const LocalWorkspaceSourceStateSchema = Schema.Struct({ | ||
| 22 | status: SourceStatusSchema, | ||
| 23 | lastError: Schema.NullOr(Schema.String), | ||
| 24 | sourceHash: Schema.NullOr(Schema.String), | ||
| 25 | createdAt: TimestampMsSchema, | ||
| 26 | updatedAt: TimestampMsSchema, | ||
| 27 | }); | ||
| 28 | |||
| 29 | const LocalWorkspacePolicyStateSchema = Schema.Struct({ | ||
| 30 | id: PolicyIdSchema, | ||
| 31 | createdAt: TimestampMsSchema, | ||
| 32 | updatedAt: TimestampMsSchema, | ||
| 33 | }); | ||
| 34 | |||
| 35 | export const LocalWorkspaceStateSchema = Schema.Struct({ | ||
| 36 | version: Schema.Literal(1), | ||
| 37 | sources: Schema.Record({ | ||
| 38 | key: Schema.String, | ||
| 39 | value: LocalWorkspaceSourceStateSchema, | ||
| 40 | }), | ||
| 41 | policies: Schema.Record({ | ||
| 42 | key: Schema.String, | ||
| 43 | value: LocalWorkspacePolicyStateSchema, | ||
| 44 | }), | ||
| 45 | }); | ||
| 46 | |||
| 47 | export type LocalWorkspaceSourceState = typeof LocalWorkspaceSourceStateSchema.Type; | ||
| 48 | export type LocalWorkspacePolicyState = typeof LocalWorkspacePolicyStateSchema.Type; | ||
| 49 | export type LocalWorkspaceState = typeof LocalWorkspaceStateSchema.Type; | ||
| 50 | |||
| 51 | const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalWorkspaceStateSchema); | 20 | const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalWorkspaceStateSchema); |
| 52 | 21 | ||
| 53 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | 22 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => |
| @@ -58,12 +27,6 @@ const mapFileSystemError = (path: string, action: string) => (cause: unknown) => · 4 unmodified lines | |||
| 58 | details: unknownLocalErrorDetails(cause), | 27 | details: unknownLocalErrorDetails(cause), |
| 59 | }); | 28 | }); |
| 60 | 29 | ||
| 61 | const defaultLocalWorkspaceState = (): LocalWorkspaceState => ({ | ||
| 62 | version: 1, | ||
| 63 | sources: {}, | ||
| 64 | policies: {}, | ||
| 65 | }); | ||
| 66 | |||
| 67 | export const localWorkspaceStatePath = ( | 30 | export const localWorkspaceStatePath = ( |
| 68 | context: ResolvedLocalWorkspaceContext, | 31 | context: ResolvedLocalWorkspaceContext, |
| 69 | ): string => join(context.stateDirectory, WORKSPACE_STATE_BASENAME); | 32 | ): string => join(context.stateDirectory, WORKSPACE_STATE_BASENAME); |
| @@ -90,13 +53,12 @@ export const loadLocalWorkspaceState = ( · 20 unmodified lines | |||
| 90 | ); | 53 | ); |
| 91 | return yield* Effect.try({ | 54 | return yield* Effect.try({ |
| 92 | try: () => decodeLocalWorkspaceState(JSON.parse(content) as unknown), | 55 | try: () => decodeLocalWorkspaceState(JSON.parse(content) as unknown), |
| 93 | catch: (cause) => { | 56 | catch: (cause) => |
| 94 | return new LocalWorkspaceStateDecodeError({ | 57 | new LocalWorkspaceStateDecodeError({ |
| 95 | message: `Invalid local workspace state at ${path}: ${unknownLocalErrorDetails(cause)}`, | 58 | message: `Invalid local workspace state at ${path}: ${unknownLocalErrorDetails(cause)}`, |
| 96 | path, | 59 | path, |
| 97 | details: unknownLocalErrorDetails(cause), | 60 | details: unknownLocalErrorDetails(cause), |
| 98 | }); | 61 | }), |
| 99 | }, | ||
| 100 | }); | 62 | }); |
| 101 | }); | 63 | }); |
| 102 | 64 | ||
| @@ -107,12 +69,12 @@ export const writeLocalWorkspaceState = (input: { · 4 unmodified lines | |||
| 107 | Effect.gen(function* () { | 69 | Effect.gen(function* () { |
| 108 | const fs = yield* FileSystem.FileSystem; | 70 | const fs = yield* FileSystem.FileSystem; |
| 109 | yield* fs.makeDirectory(input.context.stateDirectory, { recursive: true }).pipe( | 71 | yield* fs.makeDirectory(input.context.stateDirectory, { recursive: true }).pipe( |
| 110 | Effect.mapError(mapFileSystemError(input.context.stateDirectory, "create state directory")), | 72 | Effect.mapError( |
| 73 | mapFileSystemError(input.context.stateDirectory, "create state directory"), | ||
| 74 | ), | ||
| 111 | ); | 75 | ); |
| 112 | yield* fs.writeFileString( | 76 | const path = localWorkspaceStatePath(input.context); |
| 113 | localWorkspaceStatePath(input.context), | 77 | yield* fs.writeFileString(path, `${JSON.stringify(input.state, null, 2)}\n`).pipe( |
| 114 | `${JSON.stringify(input.state, null, 2)}\n`, | 78 | Effect.mapError(mapFileSystemError(path, "write workspace state")), |
| 115 | ).pipe( | ||
| 116 | Effect.mapError(mapFileSystemError(localWorkspaceStatePath(input.context), "write workspace state")), | ||
| 117 | ); | 79 | ); |
| 118 | }); | 80 | }); |
| @@ -0,0 +1,13 @@ | |||
| 1 | { | ||
| 2 | "compilerOptions": { | ||
| 3 | "target": "ES2022", | ||
| 4 | "module": "ESNext", | ||
| 5 | "moduleResolution": "Bundler", | ||
| 6 | "strict": true, | ||
| 7 | "skipLibCheck": true, | ||
| 8 | "noEmit": true, | ||
| 9 | "lib": ["ES2022"], | ||
| 10 | "types": ["bun-types", "node"] | ||
| 11 | }, | ||
| 12 | "include": ["src/**/*.ts"] | ||
| 13 | } | ||
| @@ -18,7 +18,6 @@ · 17 unmodified lines | |||
| 18 | "#schema": "./src/schema/index.ts" | 18 | "#schema": "./src/schema/index.ts" |
| 19 | }, | 19 | }, |
| 20 | "scripts": { | 20 | "scripts": { |
| 21 | "fixture:release:capture": "bun run ./src/runtime/local/capture-release-workspace-fixture.ts", | ||
| 22 | "test": "vitest run", | 21 | "test": "vitest run", |
| 23 | "test:watch": "vitest", | 22 | "test:watch": "vitest", |
| 24 | "typecheck": "bunx tsc --noEmit -p tsconfig.json" | 23 | "typecheck": "bunx tsc --noEmit -p tsconfig.json" |
| @@ -12,6 +12,10 @@ import { · 11 unmodified lines | |||
| 12 | type RuntimeControlPlaneServices, | 12 | type RuntimeControlPlaneServices, |
| 13 | type RuntimeSecretMaterialServices, | 13 | type RuntimeSecretMaterialServices, |
| 14 | } from "./runtime"; | 14 | } from "./runtime"; |
| 15 | export type { | ||
| 16 | ExecutorWorkspaceContext, | ||
| 17 | ExecutorWorkspaceDescriptor, | ||
| 18 | } from "./workspace"; | ||
| 15 | 19 | ||
| 16 | export type ExecutorBackend = { | 20 | export type ExecutorBackend = { |
| 17 | createRuntime: ( | 21 | createRuntime: ( |
| @@ -14,12 +14,7 @@ import * as Option from "effect/Option"; · 13 unmodified lines | |||
| 14 | import { submitExecutionInteractionResponse } from "../runtime/execution/service"; | 14 | import { submitExecutionInteractionResponse } from "../runtime/execution/service"; |
| 15 | import { LiveExecutionManagerService } from "../runtime/execution/live"; | 15 | import { LiveExecutionManagerService } from "../runtime/execution/live"; |
| 16 | import { operationErrors } from "../runtime/policy/operation-errors"; | 16 | import { operationErrors } from "../runtime/policy/operation-errors"; |
| 17 | import { resolveLocalWorkspaceContext } from "../runtime/local/config"; | 17 | import { InstallationStore } from "../runtime/workspace/storage"; |
| 18 | import { | ||
| 19 | InstallationStore, | ||
| 20 | LocalInstallationStore, | ||
| 21 | } from "../runtime/local/storage"; | ||
| 22 | import { getRuntimeLocalWorkspaceOption } from "../runtime/local/runtime-context"; | ||
| 23 | import { RuntimeSourceAuthServiceTag } from "../runtime/sources/source-auth-service"; | 18 | import { RuntimeSourceAuthServiceTag } from "../runtime/sources/source-auth-service"; |
| 24 | import { | 19 | import { |
| 25 | createSourceCredentialSelectionBearerContent, | 20 | createSourceCredentialSelectionBearerContent, |
| @@ -190,24 +185,15 @@ const loadSourceCredentialInteraction = (input: { · 164 unmodified lines | |||
| 190 | 185 | ||
| 191 | export const getLocalInstallation = () => | 186 | export const getLocalInstallation = () => |
| 192 | Effect.gen(function* () { | 187 | Effect.gen(function* () { |
| 193 | const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); | 188 | const installationStore = yield* InstallationStore; |
| 194 | const context = | 189 | return yield* installationStore.load().pipe( |
| 195 | runtimeLocalWorkspace?.context ?? | 190 | Effect.mapError((error) => |
| 196 | (yield* resolveLocalWorkspaceContext().pipe( | 191 | localOps.installation.unknownStorage( |
| 197 | Effect.mapError((error) => | 192 | error, |
| 198 | localOps.installation.unknownStorage( | 193 | "Failed loading local installation", |
| 199 | error, | ||
| 200 | "Failed resolving local workspace context", | ||
| 201 | ), | ||
| 202 | ), | 194 | ), |
| 203 | )); | 195 | ), |
| 204 | 196 | ); | |
| 205 | const installationStore = | ||
| 206 | runtimeLocalWorkspace !== null | ||
| 207 | ? yield* InstallationStore | ||
| 208 | : LocalInstallationStore; | ||
| 209 | |||
| 210 | return yield* installationStore.load(context); | ||
| 211 | }); | 197 | }); |
| 212 | 198 | ||
| 213 | export const getSourceCredentialInteraction = (input: { | 199 | export const getSourceCredentialInteraction = (input: { |
| @@ -16,22 +16,16 @@ import { · 15 unmodified lines | |||
| 16 | ControlPlaneNotFoundError, | 16 | ControlPlaneNotFoundError, |
| 17 | ControlPlaneStorageError, | 17 | ControlPlaneStorageError, |
| 18 | } from "../errors"; | 18 | } from "../errors"; |
| 19 | import { requireRuntimeLocalWorkspace } from "../runtime/local/runtime-context"; | 19 | import { requireRuntimeLocalWorkspace } from "../runtime/workspace/runtime-context"; |
| 20 | import { | 20 | import { |
| 21 | createDefaultSecretMaterialDeleter, | 21 | LocalInstanceConfigService, |
| 22 | createDefaultSecretMaterialStorer, | 22 | SecretMaterialDeleterService, |
| 23 | createDefaultSecretMaterialUpdater, | 23 | SecretMaterialStorerService, |
| 24 | ENV_SECRET_PROVIDER_ID, | 24 | SecretMaterialUpdaterService, |
| 25 | KEYCHAIN_SECRET_PROVIDER_ID, | 25 | } from "../runtime/workspace/secret-material-providers"; |
| 26 | LOCAL_SECRET_PROVIDER_ID, | ||
| 27 | parseSecretStoreProviderId, | ||
| 28 | resolveDefaultSecretStoreProviderId, | ||
| 29 | } from "../runtime/local/secret-material-providers"; | ||
| 30 | import { RuntimeSourceStoreService } from "../runtime/sources/source-store"; | 26 | import { RuntimeSourceStoreService } from "../runtime/sources/source-store"; |
| 31 | import { ControlPlaneStore } from "../runtime/store"; | 27 | import { ControlPlaneStore } from "../runtime/store"; |
| 32 | 28 | ||
| 33 | const SECRET_STORE_PROVIDER_ENV = "EXECUTOR_SECRET_STORE_PROVIDER"; | ||
| 34 | |||
| 35 | const secretStorageError = (operation: string, message: string) => | 29 | const secretStorageError = (operation: string, message: string) => |
| 36 | new ControlPlaneStorageError({ | 30 | new ControlPlaneStorageError({ |
| 37 | operation, | 31 | operation, |
| @@ -39,45 +33,8 @@ const secretStorageError = (operation: string, message: string) => · 1 unmodified line | |||
| 39 | details: message, | 33 | details: message, |
| 40 | }); | 34 | }); |
| 41 | 35 | ||
| 42 | export const getLocalInstanceConfig = (): Effect.Effect<InstanceConfig> => { | 36 | export const getLocalInstanceConfig = (): Effect.Effect<InstanceConfig, Error, LocalInstanceConfigService> => |
| 43 | const explicitDefaultStoreProvider = parseSecretStoreProviderId( | 37 | Effect.flatMap(LocalInstanceConfigService, (resolveInstanceConfig) => resolveInstanceConfig()); |
| 44 | process.env[SECRET_STORE_PROVIDER_ENV], | ||
| 45 | ); | ||
| 46 | const providers = [ | ||
| 47 | { | ||
| 48 | id: LOCAL_SECRET_PROVIDER_ID, | ||
| 49 | name: "Local store", | ||
| 50 | canStore: true, | ||
| 51 | }, | ||
| 52 | ]; | ||
| 53 | |||
| 54 | if (process.platform === "darwin" || process.platform === "linux") { | ||
| 55 | providers.push({ | ||
| 56 | id: KEYCHAIN_SECRET_PROVIDER_ID, | ||
| 57 | name: | ||
| 58 | process.platform === "darwin" ? "macOS Keychain" : "Desktop Keyring", | ||
| 59 | canStore: | ||
| 60 | process.platform === "darwin" || | ||
| 61 | explicitDefaultStoreProvider === KEYCHAIN_SECRET_PROVIDER_ID, | ||
| 62 | }); | ||
| 63 | } | ||
| 64 | |||
| 65 | providers.push({ | ||
| 66 | id: ENV_SECRET_PROVIDER_ID, | ||
| 67 | name: "Environment variable", | ||
| 68 | canStore: false, | ||
| 69 | }); | ||
| 70 | |||
| 71 | return resolveDefaultSecretStoreProviderId({ | ||
| 72 | storeProviderId: explicitDefaultStoreProvider ?? undefined, | ||
| 73 | }).pipe( | ||
| 74 | Effect.map((resolvedDefaultStoreProvider) => ({ | ||
| 75 | platform: process.platform, | ||
| 76 | secretProviders: providers, | ||
| 77 | defaultSecretStoreProvider: resolvedDefaultStoreProvider, | ||
| 78 | })), | ||
| 79 | ); | ||
| 80 | }; | ||
| 81 | 38 | ||
| 82 | export const listLocalSecrets = () => | 39 | export const listLocalSecrets = () => |
| 83 | Effect.gen(function* () { | 40 | Effect.gen(function* () { |
| @@ -119,10 +76,6 @@ export const createLocalSecret = (payload: CreateSecretPayload) => · 35 unmodified lines | |||
| 119 | const name = payload.name.trim(); | 76 | const name = payload.name.trim(); |
| 120 | const value = payload.value; | 77 | const value = payload.value; |
| 121 | const purpose = payload.purpose ?? "auth_material"; | 78 | const purpose = payload.purpose ?? "auth_material"; |
| 122 | const requestedProviderId = | ||
| 123 | payload.providerId === undefined | ||
| 124 | ? null | ||
| 125 | : parseSecretStoreProviderId(payload.providerId); | ||
| 126 | 79 | ||
| 127 | if (name.length === 0) { | 80 | if (name.length === 0) { |
| 128 | return yield* new ControlPlaneBadRequestError({ | 81 | return yield* new ControlPlaneBadRequestError({ |
| @@ -131,23 +84,14 @@ export const createLocalSecret = (payload: CreateSecretPayload) => · 2 unmodified lines | |||
| 131 | details: "Secret name is required.", | 84 | details: "Secret name is required.", |
| 132 | }); | 85 | }); |
| 133 | } | 86 | } |
| 134 | if (payload.providerId !== undefined && requestedProviderId === null) { | ||
| 135 | return yield* new ControlPlaneBadRequestError({ | ||
| 136 | operation: "secrets.create", | ||
| 137 | message: `Unsupported secret provider: ${payload.providerId}`, | ||
| 138 | details: `Unsupported secret provider: ${payload.providerId}`, | ||
| 139 | }); | ||
| 140 | } | ||
| 141 | 87 | ||
| 142 | const store = yield* ControlPlaneStore; | 88 | const store = yield* ControlPlaneStore; |
| 143 | const storeSecretMaterial = createDefaultSecretMaterialStorer({ | 89 | const storeSecretMaterial = yield* SecretMaterialStorerService; |
| 144 | rows: store, | ||
| 145 | ...(requestedProviderId ? { storeProviderId: requestedProviderId } : {}), | ||
| 146 | }); | ||
| 147 | const ref = yield* storeSecretMaterial({ | 90 | const ref = yield* storeSecretMaterial({ |
| 148 | name, | 91 | name, |
| 149 | purpose, | 92 | purpose, |
| 150 | value, | 93 | value, |
| 94 | providerId: payload.providerId, | ||
| 151 | }).pipe( | 95 | }).pipe( |
| 152 | Effect.mapError((cause) => | 96 | Effect.mapError((cause) => |
| 153 | secretStorageError( | 97 | secretStorageError( |
| @@ -214,9 +158,7 @@ export const updateLocalSecret = (input: { · 60 unmodified lines | |||
| 214 | update.name = input.payload.name.trim() || null; | 158 | update.name = input.payload.name.trim() || null; |
| 215 | if (input.payload.value !== undefined) update.value = input.payload.value; | 159 | if (input.payload.value !== undefined) update.value = input.payload.value; |
| 216 | 160 | ||
| 217 | const updateSecretMaterial = createDefaultSecretMaterialUpdater({ | 161 | const updateSecretMaterial = yield* SecretMaterialUpdaterService; |
| 218 | rows: store, | ||
| 219 | }); | ||
| 220 | const updated = yield* updateSecretMaterial({ | 162 | const updated = yield* updateSecretMaterial({ |
| 221 | ref: { | 163 | ref: { |
| 222 | providerId: existing.value.providerId, | 164 | providerId: existing.value.providerId, |
| @@ -260,9 +202,7 @@ export const deleteLocalSecret = (secretId: string) => · 37 unmodified lines | |||
| 260 | }); | 202 | }); |
| 261 | } | 203 | } |
| 262 | 204 | ||
| 263 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | 205 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; |
| 264 | rows: store, | ||
| 265 | }); | ||
| 266 | const removed = yield* deleteSecretMaterial({ | 206 | const removed = yield* deleteSecretMaterial({ |
| 267 | providerId: existing.value.providerId, | 207 | providerId: existing.value.providerId, |
| 268 | handle: existing.value.id, | 208 | handle: existing.value.id, |
| @@ -13,18 +13,17 @@ import { · 12 unmodified lines | |||
| 13 | } from "../schema"; | 13 | } from "../schema"; |
| 14 | import * as Effect from "effect/Effect"; | 14 | import * as Effect from "effect/Effect"; |
| 15 | 15 | ||
| 16 | import { requireRuntimeLocalWorkspace } from "../runtime/local/runtime-context"; | 16 | import { requireRuntimeLocalWorkspace } from "../runtime/workspace/runtime-context"; |
| 17 | import type { | 17 | import type { |
| 18 | WorkspaceConfigStoreShape, | 18 | WorkspaceConfigStoreShape, |
| 19 | WorkspaceStateStoreShape, | 19 | WorkspaceStateStoreShape, |
| 20 | } from "../runtime/local/storage"; | 20 | } from "../runtime/workspace/storage"; |
| 21 | import { | 21 | import { |
| 22 | WorkspaceConfigStore, | 22 | WorkspaceConfigStore, |
| 23 | WorkspaceStateStore, | 23 | WorkspaceStateStore, |
| 24 | } from "../runtime/local/storage"; | 24 | } from "../runtime/workspace/storage"; |
| 25 | import type { ResolvedLocalWorkspaceContext } from "../runtime/local/config"; | 25 | import { type LocalWorkspaceState } from "../runtime/workspace-state"; |
| 26 | import { type LocalWorkspaceState } from "../runtime/local/workspace-state"; | 26 | import { derivePolicyConfigKey } from "../runtime/workspace/workspace-sync"; |
| 27 | import { derivePolicyConfigKey } from "../runtime/local/workspace-sync"; | ||
| 28 | import { | 27 | import { |
| 29 | type OperationErrors, | 28 | type OperationErrors, |
| 30 | operationErrors, | 29 | operationErrors, |
| @@ -40,17 +39,22 @@ const policyOps = { · 9 unmodified lines | |||
| 40 | 39 | ||
| 41 | const cloneJson = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T; | 40 | const cloneJson = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T; |
| 42 | 41 | ||
| 42 | const workspacePolicyStableKey = (input: { | ||
| 43 | workspaceId: WorkspaceId; | ||
| 44 | workspaceRoot?: string | null; | ||
| 45 | }): string => input.workspaceRoot?.trim() || input.workspaceId; | ||
| 46 | |||
| 43 | const localPolicyIdForKey = (input: { | 47 | const localPolicyIdForKey = (input: { |
| 44 | workspaceRoot: string; | 48 | workspaceStableKey: string; |
| 45 | key: string; | 49 | key: string; |
| 46 | }): PolicyId => | 50 | }): PolicyId => |
| 47 | PolicyIdSchema.make( | 51 | PolicyIdSchema.make( |
| 48 | `pol_local_${createHash("sha256").update(`${input.workspaceRoot}:${input.key}`).digest("hex").slice(0, 16)}`, | 52 | `pol_local_${createHash("sha256").update(`${input.workspaceStableKey}:${input.key}`).digest("hex").slice(0, 16)}`, |
| 49 | ); | 53 | ); |
| 50 | 54 | ||
| 51 | const toLocalWorkspacePolicy = (input: { | 55 | const toLocalWorkspacePolicy = (input: { |
| 52 | workspaceId: WorkspaceId; | 56 | workspaceId: WorkspaceId; |
| 53 | workspaceRoot: string; | 57 | workspaceStableKey: string; |
| 54 | key: string; | 58 | key: string; |
| 55 | policyConfig: NonNullable<LocalExecutorConfig["policies"]>[string]; | 59 | policyConfig: NonNullable<LocalExecutorConfig["policies"]>[string]; |
| 56 | state: LocalWorkspaceState["policies"][string] | undefined; | 60 | state: LocalWorkspaceState["policies"][string] | undefined; |
| @@ -58,7 +62,7 @@ const toLocalWorkspacePolicy = (input: { · 1 unmodified line | |||
| 58 | id: | 62 | id: |
| 59 | input.state?.id ?? | 63 | input.state?.id ?? |
| 60 | localPolicyIdForKey({ | 64 | localPolicyIdForKey({ |
| 61 | workspaceRoot: input.workspaceRoot, | 65 | workspaceStableKey: input.workspaceStableKey, |
| 62 | key: input.key, | 66 | key: input.key, |
| 63 | }), | 67 | }), |
| 64 | key: input.key, | 68 | key: input.key, |
| @@ -78,18 +82,17 @@ export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => · 13 unmodified lines | |||
| 78 | yield* requireRuntimeLocalWorkspace(workspaceId); | 82 | yield* requireRuntimeLocalWorkspace(workspaceId); |
| 79 | const workspaceConfigStore = yield* WorkspaceConfigStore; | 83 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 80 | const workspaceStateStore = yield* WorkspaceStateStore; | 84 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 81 | const loadedConfig = yield* workspaceConfigStore.load( | 85 | const loadedConfig = yield* workspaceConfigStore.load(); |
| 82 | runtimeLocalWorkspace.context, | 86 | const workspaceState = yield* workspaceStateStore.load(); |
| 83 | ); | ||
| 84 | const workspaceState = yield* workspaceStateStore.load( | ||
| 85 | runtimeLocalWorkspace.context, | ||
| 86 | ); | ||
| 87 | 87 | ||
| 88 | const policies = Object.entries(loadedConfig.config?.policies ?? {}).map( | 88 | const policies = Object.entries(loadedConfig.config?.policies ?? {}).map( |
| 89 | ([key, policyConfig]) => | 89 | ([key, policyConfig]) => |
| 90 | toLocalWorkspacePolicy({ | 90 | toLocalWorkspacePolicy({ |
| 91 | workspaceId, | 91 | workspaceId, |
| 92 | workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | 92 | workspaceStableKey: workspacePolicyStableKey({ |
| 93 | workspaceId, | ||
| 94 | workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | ||
| 95 | }), | ||
| 93 | key, | 96 | key, |
| 94 | policyConfig, | 97 | policyConfig, |
| 95 | state: workspaceState.policies[key], | 98 | state: workspaceState.policies[key], |
| @@ -106,7 +109,6 @@ export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => · 10 unmodified lines | |||
| 106 | 109 | ||
| 107 | const writeLocalPolicyFiles = (input: { | 110 | const writeLocalPolicyFiles = (input: { |
| 108 | operation: OperationErrors; | 111 | operation: OperationErrors; |
| 109 | context: ResolvedLocalWorkspaceContext; | ||
| 110 | workspaceConfigStore: WorkspaceConfigStoreShape; | 112 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 111 | workspaceStateStore: WorkspaceStateStoreShape; | 113 | workspaceStateStore: WorkspaceStateStoreShape; |
| 112 | projectConfig: LocalExecutorConfig; | 114 | projectConfig: LocalExecutorConfig; |
| @@ -115,11 +117,9 @@ const writeLocalPolicyFiles = (input: { · 2 unmodified lines | |||
| 115 | Effect.all( | 117 | Effect.all( |
| 116 | [ | 118 | [ |
| 117 | input.workspaceConfigStore.writeProject({ | 119 | input.workspaceConfigStore.writeProject({ |
| 118 | context: input.context, | ||
| 119 | config: input.projectConfig, | 120 | config: input.projectConfig, |
| 120 | }), | 121 | }), |
| 121 | input.workspaceStateStore.write({ | 122 | input.workspaceStateStore.write({ |
| 122 | context: input.context, | ||
| 123 | state: input.workspaceState, | 123 | state: input.workspaceState, |
| 124 | }), | 124 | }), |
| 125 | ], | 125 | ], |
| @@ -219,7 +219,10 @@ export const createPolicy = (input: { · 93 unmodified lines | |||
| 219 | id: | 219 | id: |
| 220 | existingState?.id ?? | 220 | existingState?.id ?? |
| 221 | localPolicyIdForKey({ | 221 | localPolicyIdForKey({ |
| 222 | workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | 222 | workspaceStableKey: workspacePolicyStableKey({ |
| 223 | workspaceId: input.workspaceId, | ||
| 224 | workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | ||
| 225 | }), | ||
| 223 | key, | 226 | key, |
| 224 | }), | 227 | }), |
| 225 | createdAt: existingState?.createdAt ?? now, | 228 | createdAt: existingState?.createdAt ?? now, |
| @@ -230,7 +233,6 @@ export const createPolicy = (input: { · 4 unmodified lines | |||
| 230 | 233 | ||
| 231 | yield* writeLocalPolicyFiles({ | 234 | yield* writeLocalPolicyFiles({ |
| 232 | operation: policyOps.create, | 235 | operation: policyOps.create, |
| 233 | context: runtimeLocalWorkspace.context, | ||
| 234 | workspaceConfigStore, | 236 | workspaceConfigStore, |
| 235 | workspaceStateStore, | 237 | workspaceStateStore, |
| 236 | projectConfig: { | 238 | projectConfig: { |
| @@ -242,7 +244,10 @@ export const createPolicy = (input: { · 5 unmodified lines | |||
| 242 | 244 | ||
| 243 | return toLocalWorkspacePolicy({ | 245 | return toLocalWorkspacePolicy({ |
| 244 | workspaceId: input.workspaceId, | 246 | workspaceId: input.workspaceId, |
| 245 | workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | 247 | workspaceStableKey: workspacePolicyStableKey({ |
| 248 | workspaceId: input.workspaceId, | ||
| 249 | workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | ||
| 250 | }), | ||
| 246 | key, | 251 | key, |
| 247 | policyConfig: policies[key]!, | 252 | policyConfig: policies[key]!, |
| 248 | state: workspaceState.policies[key], | 253 | state: workspaceState.policies[key], |
| @@ -355,7 +360,6 @@ export const updatePolicy = (input: { · 106 unmodified lines | |||
| 355 | 360 | ||
| 356 | yield* writeLocalPolicyFiles({ | 361 | yield* writeLocalPolicyFiles({ |
| 357 | operation: policyOps.update, | 362 | operation: policyOps.update, |
| 358 | context: runtimeLocalWorkspace.context, | ||
| 359 | workspaceConfigStore, | 363 | workspaceConfigStore, |
| 360 | workspaceStateStore, | 364 | workspaceStateStore, |
| 361 | projectConfig: { | 365 | projectConfig: { |
| @@ -367,7 +371,10 @@ export const updatePolicy = (input: { · 5 unmodified lines | |||
| 367 | 371 | ||
| 368 | return toLocalWorkspacePolicy({ | 372 | return toLocalWorkspacePolicy({ |
| 369 | workspaceId: input.workspaceId, | 373 | workspaceId: input.workspaceId, |
| 370 | workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | 374 | workspaceStableKey: workspacePolicyStableKey({ |
| 375 | workspaceId: input.workspaceId, | ||
| 376 | workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | ||
| 377 | }), | ||
| 371 | key: existing.key, | 378 | key: existing.key, |
| 372 | policyConfig: policies[existing.key]!, | 379 | policyConfig: policies[existing.key]!, |
| 373 | state: workspaceState.policies[existing.key], | 380 | state: workspaceState.policies[existing.key], |
| @@ -413,7 +420,6 @@ export const removePolicy = (input: { · 39 unmodified lines | |||
| 413 | localWorkspace.workspaceState.policies; | 420 | localWorkspace.workspaceState.policies; |
| 414 | yield* writeLocalPolicyFiles({ | 421 | yield* writeLocalPolicyFiles({ |
| 415 | operation: policyOps.remove, | 422 | operation: policyOps.remove, |
| 416 | context: runtimeLocalWorkspace.context, | ||
| 417 | workspaceConfigStore, | 423 | workspaceConfigStore, |
| 418 | workspaceStateStore, | 424 | workspaceStateStore, |
| 419 | projectConfig: { | 425 | projectConfig: { |
| @@ -9,7 +9,7 @@ Each fixture directory should contain: · 8 unmodified lines | |||
| 9 | - `.executor/state/workspace-state.json`: a trimmed workspace state with only the captured source | 9 | - `.executor/state/workspace-state.json`: a trimmed workspace state with only the captured source |
| 10 | - `.executor/artifacts/sources/...`: the real artifact JSON and any sidecar files that release wrote | 10 | - `.executor/artifacts/sources/...`: the real artifact JSON and any sidecar files that release wrote |
| 11 | 11 | ||
| 12 | `src/runtime/local/source-artifacts.test.ts` auto-discovers every fixture directory here that has a `fixture.json` file. Adding a new release fixture should not require changing the test. | 12 | `packages/platform/sdk-file/src/source-artifacts.test.ts` auto-discovers every fixture directory here that has a `fixture.json` file. Adding a new release fixture should not require changing the test. |
| 13 | 13 | ||
| 14 | ## Capture A New Release Fixture | 14 | ## Capture A New Release Fixture |
| 15 | 15 | ||
| @@ -35,4 +35,4 @@ On releases where you want upgrade coverage: · 19 unmodified lines | |||
| 35 | 1. Start from a real workspace that has a fully synced source on the released version. | 35 | 1. Start from a real workspace that has a fully synced source on the released version. |
| 36 | 2. Run `bun run fixture:release:capture -- ...` for that source. | 36 | 2. Run `bun run fixture:release:capture -- ...` for that source. |
| 37 | 3. Review the fixture for secrets before committing it. | 37 | 3. Review the fixture for secrets before committing it. |
| 38 | 4. Run `bun run --cwd packages/platform/sdk test -- src/runtime/local/source-artifacts.test.ts`. | 38 | 4. Run `bun run --cwd packages/platform/sdk-file test -- src/source-artifacts.test.ts`. |
| @@ -35,7 +35,7 @@ import * as Schema from "effect/Schema"; · 34 unmodified lines | |||
| 35 | import type { | 35 | import type { |
| 36 | ResolveSecretMaterial, | 36 | ResolveSecretMaterial, |
| 37 | SecretMaterialResolveContext, | 37 | SecretMaterialResolveContext, |
| 38 | } from "../local/secret-material-providers"; | 38 | } from "../workspace/secret-material-providers"; |
| 39 | import { runtimeEffectError } from "../effect-errors"; | 39 | import { runtimeEffectError } from "../effect-errors"; |
| 40 | 40 | ||
| 41 | export type ResolvedSourceAuthMaterial = { | 41 | export type ResolvedSourceAuthMaterial = { |
| @@ -19,11 +19,11 @@ import * as Option from "effect/Option"; · 18 unmodified lines | |||
| 19 | import type { ControlPlaneStoreShape } from "../store"; | 19 | import type { ControlPlaneStoreShape } from "../store"; |
| 20 | import { resolveAuthArtifactMaterial, type ResolvedSourceAuthMaterial } from "./auth-artifacts"; | 20 | import { resolveAuthArtifactMaterial, type ResolvedSourceAuthMaterial } from "./auth-artifacts"; |
| 21 | import { | 21 | import { |
| 22 | createDefaultSecretMaterialDeleter, | 22 | type DeleteSecretMaterial, |
| 23 | createDefaultSecretMaterialStorer, | ||
| 24 | type ResolveSecretMaterial, | 23 | type ResolveSecretMaterial, |
| 25 | type SecretMaterialResolveContext, | 24 | type SecretMaterialResolveContext, |
| 26 | } from "../local/secret-material-providers"; | 25 | type StoreSecretMaterial, |
| 26 | } from "../workspace/secret-material-providers"; | ||
| 27 | import { refreshOAuth2AccessToken } from "./oauth2-pkce"; | 27 | import { refreshOAuth2AccessToken } from "./oauth2-pkce"; |
| 28 | import type { OAuth2TokenResponse } from "./oauth2-pkce"; | 28 | import type { OAuth2TokenResponse } from "./oauth2-pkce"; |
| 29 | import { createPersistedMcpAuthProvider } from "./mcp-auth-provider"; | 29 | import { createPersistedMcpAuthProvider } from "./mcp-auth-provider"; |
| @@ -43,13 +43,12 @@ const cleanupAuthLeaseSecretRefs = ( · 13 unmodified lines | |||
| 43 | previous: Pick<AuthLease, "placementsTemplateJson"> | null; | 43 | previous: Pick<AuthLease, "placementsTemplateJson"> | null; |
| 44 | next: Pick<AuthLease, "placementsTemplateJson"> | null; | 44 | next: Pick<AuthLease, "placementsTemplateJson"> | null; |
| 45 | }, | 45 | }, |
| 46 | deleteSecretMaterial: DeleteSecretMaterial, | ||
| 46 | ) => | 47 | ) => |
| 47 | Effect.gen(function* () { | 48 | Effect.gen(function* () { |
| 48 | if (input.previous === null) { | 49 | if (input.previous === null) { |
| 49 | return; | 50 | return; |
| 50 | } | 51 | } |
| 51 | |||
| 52 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | ||
| 53 | const nextRefKeys = new Set( | 52 | const nextRefKeys = new Set( |
| 54 | (input.next === null ? [] : authLeaseSecretRefs(input.next)).map(secretRefKey), | 53 | (input.next === null ? [] : authLeaseSecretRefs(input.next)).map(secretRefKey), |
| 55 | ); | 54 | ); |
| @@ -85,6 +84,8 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 29 unmodified lines | |||
| 85 | artifact: AuthArtifact; | 84 | artifact: AuthArtifact; |
| 86 | lease: AuthLease | null; | 85 | lease: AuthLease | null; |
| 87 | resolveSecretMaterial: ResolveSecretMaterial; | 86 | resolveSecretMaterial: ResolveSecretMaterial; |
| 87 | storeSecretMaterial: StoreSecretMaterial; | ||
| 88 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 88 | context?: SecretMaterialResolveContext; | 89 | context?: SecretMaterialResolveContext; |
| 89 | }): Effect.Effect<AuthLease, Error, never> => | 90 | }): Effect.Effect<AuthLease, Error, never> => |
| 90 | Effect.gen(function* () { | 91 | Effect.gen(function* () { |
| @@ -116,10 +117,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 25 unmodified lines | |||
| 116 | refreshToken, | 117 | refreshToken, |
| 117 | }); | 118 | }); |
| 118 | 119 | ||
| 119 | const storeSecretMaterial = createDefaultSecretMaterialStorer({ | 120 | const accessTokenRef = yield* input.storeSecretMaterial({ |
| 120 | rows: input.rows, | ||
| 121 | }); | ||
| 122 | const accessTokenRef = yield* storeSecretMaterial({ | ||
| 123 | purpose: "oauth_access_token", | 121 | purpose: "oauth_access_token", |
| 124 | value: tokenResponse.access_token, | 122 | value: tokenResponse.access_token, |
| 125 | name: `${decoded.config.clientId} Access Token`, | 123 | name: `${decoded.config.clientId} Access Token`, |
| @@ -166,7 +164,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 40 unmodified lines | |||
| 166 | yield* cleanupAuthLeaseSecretRefs(input.rows, { | 164 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 167 | previous: input.lease, | 165 | previous: input.lease, |
| 168 | next: nextLease, | 166 | next: nextLease, |
| 169 | }); | 167 | }, input.deleteSecretMaterial); |
| 170 | 168 | ||
| 171 | return nextLease; | 169 | return nextLease; |
| 172 | }); | 170 | }); |
| @@ -185,10 +183,10 @@ const workspaceOauthClientSecretRef = (input: { · 12 unmodified lines | |||
| 185 | const cleanupProviderGrantRefreshToken = (rows: ControlPlaneStoreShape, input: { | 183 | const cleanupProviderGrantRefreshToken = (rows: ControlPlaneStoreShape, input: { |
| 186 | previous: SecretRef; | 184 | previous: SecretRef; |
| 187 | next: SecretRef; | 185 | next: SecretRef; |
| 188 | }) => | 186 | }, deleteSecretMaterial: DeleteSecretMaterial) => |
| 189 | secretRefKey(input.previous) === secretRefKey(input.next) | 187 | secretRefKey(input.previous) === secretRefKey(input.next) |
| 190 | ? Effect.void | 188 | ? Effect.void |
| 191 | : createDefaultSecretMaterialDeleter({ rows })(input.previous).pipe( | 189 | : deleteSecretMaterial(input.previous).pipe( |
| 192 | Effect.either, | 190 | Effect.either, |
| 193 | Effect.ignore, | 191 | Effect.ignore, |
| 194 | ); | 192 | ); |
| @@ -198,6 +196,8 @@ const refreshProviderGrantRefArtifact = (input: { · 3 unmodified lines | |||
| 198 | artifact: AuthArtifact; | 196 | artifact: AuthArtifact; |
| 199 | lease: AuthLease | null; | 197 | lease: AuthLease | null; |
| 200 | resolveSecretMaterial: ResolveSecretMaterial; | 198 | resolveSecretMaterial: ResolveSecretMaterial; |
| 199 | storeSecretMaterial: StoreSecretMaterial; | ||
| 200 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 201 | context?: SecretMaterialResolveContext; | 201 | context?: SecretMaterialResolveContext; |
| 202 | }): Effect.Effect<AuthLease, Error, never> => | 202 | }): Effect.Effect<AuthLease, Error, never> => |
| 203 | Effect.gen(function* () { | 203 | Effect.gen(function* () { |
| @@ -239,17 +239,14 @@ const refreshProviderGrantRefArtifact = (input: { · 35 unmodified lines | |||
| 239 | scopes: config.requiredScopes.length > 0 ? config.requiredScopes : null, | 239 | scopes: config.requiredScopes.length > 0 ? config.requiredScopes : null, |
| 240 | }); | 240 | }); |
| 241 | 241 | ||
| 242 | const storeSecretMaterial = createDefaultSecretMaterialStorer({ | 242 | const accessTokenRef = yield* input.storeSecretMaterial({ |
| 243 | rows: input.rows, | ||
| 244 | }); | ||
| 245 | const accessTokenRef = yield* storeSecretMaterial({ | ||
| 246 | purpose: "oauth_access_token", | 243 | purpose: "oauth_access_token", |
| 247 | value: tokenResponse.access_token, | 244 | value: tokenResponse.access_token, |
| 248 | name: `${grant.providerKey} Access Token`, | 245 | name: `${grant.providerKey} Access Token`, |
| 249 | }); | 246 | }); |
| 250 | 247 | ||
| 251 | const rotatedRefreshTokenRef = tokenResponse.refresh_token | 248 | const rotatedRefreshTokenRef = tokenResponse.refresh_token |
| 252 | ? yield* storeSecretMaterial({ | 249 | ? yield* input.storeSecretMaterial({ |
| 253 | purpose: "oauth_refresh_token", | 250 | purpose: "oauth_refresh_token", |
| 254 | value: tokenResponse.refresh_token, | 251 | value: tokenResponse.refresh_token, |
| 255 | name: `${grant.providerKey} Refresh Token`, | 252 | name: `${grant.providerKey} Refresh Token`, |
| @@ -267,7 +264,7 @@ const refreshProviderGrantRefArtifact = (input: { · 11 unmodified lines | |||
| 267 | yield* cleanupProviderGrantRefreshToken(input.rows, { | 264 | yield* cleanupProviderGrantRefreshToken(input.rows, { |
| 268 | previous: grant.refreshToken, | 265 | previous: grant.refreshToken, |
| 269 | next: rotatedRefreshTokenRef, | 266 | next: rotatedRefreshTokenRef, |
| 270 | }); | 267 | }, input.deleteSecretMaterial); |
| 271 | 268 | ||
| 272 | const expiresInMs = | 269 | const expiresInMs = |
| 273 | typeof tokenResponse.expires_in === "number" && Number.isFinite(tokenResponse.expires_in) | 270 | typeof tokenResponse.expires_in === "number" && Number.isFinite(tokenResponse.expires_in) |
| @@ -310,14 +307,14 @@ const refreshProviderGrantRefArtifact = (input: { · 36 unmodified lines | |||
| 310 | yield* cleanupAuthLeaseSecretRefs(input.rows, { | 307 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 311 | previous: input.lease, | 308 | previous: input.lease, |
| 312 | next: nextLease, | 309 | next: nextLease, |
| 313 | }); | 310 | }, input.deleteSecretMaterial); |
| 314 | 311 | ||
| 315 | return nextLease; | 312 | return nextLease; |
| 316 | }); | 313 | }); |
| 317 | 314 | ||
| 318 | export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { | 315 | export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { |
| 319 | authArtifactId: AuthArtifact["id"]; | 316 | authArtifactId: AuthArtifact["id"]; |
| 320 | }): Effect.Effect<void, Error, never> => | 317 | }, deleteSecretMaterial: DeleteSecretMaterial): Effect.Effect<void, Error, never> => |
| 321 | Effect.gen(function* () { | 318 | Effect.gen(function* () { |
| 322 | const existingLease = yield* rows.authLeases.getByAuthArtifactId(input.authArtifactId); | 319 | const existingLease = yield* rows.authLeases.getByAuthArtifactId(input.authArtifactId); |
| 323 | if (Option.isNone(existingLease)) { | 320 | if (Option.isNone(existingLease)) { |
| @@ -328,13 +325,15 @@ export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { · 4 unmodified lines | |||
| 328 | yield* cleanupAuthLeaseSecretRefs(rows, { | 325 | yield* cleanupAuthLeaseSecretRefs(rows, { |
| 329 | previous: existingLease.value, | 326 | previous: existingLease.value, |
| 330 | next: null, | 327 | next: null, |
| 331 | }); | 328 | }, deleteSecretMaterial); |
| 332 | }); | 329 | }); |
| 333 | 330 | ||
| 334 | export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { | 331 | export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { |
| 335 | rows: ControlPlaneStoreShape; | 332 | rows: ControlPlaneStoreShape; |
| 336 | artifact: AuthArtifact; | 333 | artifact: AuthArtifact; |
| 337 | tokenResponse: OAuth2TokenResponse; | 334 | tokenResponse: OAuth2TokenResponse; |
| 335 | storeSecretMaterial: StoreSecretMaterial; | ||
| 336 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 338 | }): Effect.Effect<void, Error, never> => | 337 | }): Effect.Effect<void, Error, never> => |
| 339 | Effect.gen(function* () { | 338 | Effect.gen(function* () { |
| 340 | const decoded = decodeBuiltInAuthArtifactConfig(input.artifact); | 339 | const decoded = decodeBuiltInAuthArtifactConfig(input.artifact); |
| @@ -347,10 +346,7 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 6 unmodified lines | |||
| 347 | 346 | ||
| 348 | const existingLeaseOption = yield* input.rows.authLeases.getByAuthArtifactId(input.artifact.id); | 347 | const existingLeaseOption = yield* input.rows.authLeases.getByAuthArtifactId(input.artifact.id); |
| 349 | const existingLease = Option.isSome(existingLeaseOption) ? existingLeaseOption.value : null; | 348 | const existingLease = Option.isSome(existingLeaseOption) ? existingLeaseOption.value : null; |
| 350 | const storeSecretMaterial = createDefaultSecretMaterialStorer({ | 349 | const accessTokenRef = yield* input.storeSecretMaterial({ |
| 351 | rows: input.rows, | ||
| 352 | }); | ||
| 353 | const accessTokenRef = yield* storeSecretMaterial({ | ||
| 354 | purpose: "oauth_access_token", | 350 | purpose: "oauth_access_token", |
| 355 | value: input.tokenResponse.access_token, | 351 | value: input.tokenResponse.access_token, |
| 356 | name: `${decoded.config.clientId} Access Token`, | 352 | name: `${decoded.config.clientId} Access Token`, |
| @@ -397,13 +393,15 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 40 unmodified lines | |||
| 397 | yield* cleanupAuthLeaseSecretRefs(input.rows, { | 393 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 398 | previous: existingLease, | 394 | previous: existingLease, |
| 399 | next: nextLease, | 395 | next: nextLease, |
| 400 | }); | 396 | }, input.deleteSecretMaterial); |
| 401 | }); | 397 | }); |
| 402 | 398 | ||
| 403 | export const resolveAuthArtifactMaterialWithLeases = (input: { | 399 | export const resolveAuthArtifactMaterialWithLeases = (input: { |
| 404 | rows: ControlPlaneStoreShape; | 400 | rows: ControlPlaneStoreShape; |
| 405 | artifact: AuthArtifact | null; | 401 | artifact: AuthArtifact | null; |
| 406 | resolveSecretMaterial: ResolveSecretMaterial; | 402 | resolveSecretMaterial: ResolveSecretMaterial; |
| 403 | storeSecretMaterial: StoreSecretMaterial; | ||
| 404 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 407 | context?: SecretMaterialResolveContext; | 405 | context?: SecretMaterialResolveContext; |
| 408 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => | 406 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => |
| 409 | Effect.gen(function* () { | 407 | Effect.gen(function* () { |
| @@ -432,6 +430,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 22 unmodified lines | |||
| 432 | artifact: input.artifact, | 430 | artifact: input.artifact, |
| 433 | lease: existingLease, | 431 | lease: existingLease, |
| 434 | resolveSecretMaterial: input.resolveSecretMaterial, | 432 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 433 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 434 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 435 | context: input.context, | 435 | context: input.context, |
| 436 | }); | 436 | }); |
| 437 | 437 | ||
| @@ -451,6 +451,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 13 unmodified lines | |||
| 451 | artifact: input.artifact, | 451 | artifact: input.artifact, |
| 452 | lease: existingLease, | 452 | lease: existingLease, |
| 453 | resolveSecretMaterial: input.resolveSecretMaterial, | 453 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 454 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 455 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 454 | context: input.context, | 456 | context: input.context, |
| 455 | }); | 457 | }); |
| 456 | 458 | ||
| @@ -476,6 +478,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 19 unmodified lines | |||
| 476 | artifact: input.artifact, | 478 | artifact: input.artifact, |
| 477 | config: mcpOAuthConfig, | 479 | config: mcpOAuthConfig, |
| 478 | resolveSecretMaterial: input.resolveSecretMaterial, | 480 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 481 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 482 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 479 | context: input.context, | 483 | context: input.context, |
| 480 | }), | 484 | }), |
| 481 | }; | 485 | }; |
| @@ -22,11 +22,11 @@ import * as Schema from "effect/Schema"; · 21 unmodified lines | |||
| 22 | 22 | ||
| 23 | import type { ControlPlaneStoreShape } from "../store"; | 23 | import type { ControlPlaneStoreShape } from "../store"; |
| 24 | import { | 24 | import { |
| 25 | createDefaultSecretMaterialDeleter, | 25 | type DeleteSecretMaterial, |
| 26 | createDefaultSecretMaterialStorer, | ||
| 27 | type ResolveSecretMaterial, | 26 | type ResolveSecretMaterial, |
| 28 | type SecretMaterialResolveContext, | 27 | type SecretMaterialResolveContext, |
| 29 | } from "../local/secret-material-providers"; | 28 | type StoreSecretMaterial, |
| 29 | } from "../workspace/secret-material-providers"; | ||
| 30 | 30 | ||
| 31 | const encodeMcpOAuthAuthArtifactConfig = Schema.encodeSync( | 31 | const encodeMcpOAuthAuthArtifactConfig = Schema.encodeSync( |
| 32 | McpOAuthAuthArtifactConfigJsonSchema, | 32 | McpOAuthAuthArtifactConfigJsonSchema, |
| @@ -62,24 +62,23 @@ const secretRefEquals = (left: SecretRef | null, right: SecretRef | null): boole · 29 unmodified lines | |||
| 62 | (left?.providerId ?? null) === (right?.providerId ?? null) | 62 | (left?.providerId ?? null) === (right?.providerId ?? null) |
| 63 | && (left?.handle ?? null) === (right?.handle ?? null); | 63 | && (left?.handle ?? null) === (right?.handle ?? null); |
| 64 | 64 | ||
| 65 | const cleanupReplacedSecretRefs = (rows: ControlPlaneStoreShape, input: { | 65 | const cleanupReplacedSecretRefs = (input: { |
| 66 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 66 | previousAccessToken: SecretRef; | 67 | previousAccessToken: SecretRef; |
| 67 | nextAccessToken: SecretRef; | 68 | nextAccessToken: SecretRef; |
| 68 | previousRefreshToken: SecretRef | null; | 69 | previousRefreshToken: SecretRef | null; |
| 69 | nextRefreshToken: SecretRef | null; | 70 | nextRefreshToken: SecretRef | null; |
| 70 | }) => | 71 | }) => |
| 71 | Effect.gen(function* () { | 72 | Effect.gen(function* () { |
| 72 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | ||
| 73 | |||
| 74 | if (!secretRefEquals(input.previousAccessToken, input.nextAccessToken)) { | 73 | if (!secretRefEquals(input.previousAccessToken, input.nextAccessToken)) { |
| 75 | yield* Effect.either(deleteSecretMaterial(input.previousAccessToken)); | 74 | yield* Effect.either(input.deleteSecretMaterial(input.previousAccessToken)); |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | if ( | 77 | if ( |
| 79 | input.previousRefreshToken !== null | 78 | input.previousRefreshToken !== null |
| 80 | && !secretRefEquals(input.previousRefreshToken, input.nextRefreshToken) | 79 | && !secretRefEquals(input.previousRefreshToken, input.nextRefreshToken) |
| 81 | ) { | 80 | ) { |
| 82 | yield* Effect.either(deleteSecretMaterial(input.previousRefreshToken)); | 81 | yield* Effect.either(input.deleteSecretMaterial(input.previousRefreshToken)); |
| 83 | } | 82 | } |
| 84 | }); | 83 | }); |
| 85 | 84 | ||
| @@ -115,12 +114,10 @@ export const createPersistedMcpAuthProvider = (input: { · 29 unmodified lines | |||
| 115 | artifact: AuthArtifact; | 114 | artifact: AuthArtifact; |
| 116 | config: McpOAuthAuthArtifactConfig; | 115 | config: McpOAuthAuthArtifactConfig; |
| 117 | resolveSecretMaterial: ResolveSecretMaterial; | 116 | resolveSecretMaterial: ResolveSecretMaterial; |
| 117 | storeSecretMaterial: StoreSecretMaterial; | ||
| 118 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 118 | context?: SecretMaterialResolveContext; | 119 | context?: SecretMaterialResolveContext; |
| 119 | }): OAuthClientProvider => { | 120 | }): OAuthClientProvider => { |
| 120 | const storeSecretMaterial = createDefaultSecretMaterialStorer({ | ||
| 121 | rows: input.rows, | ||
| 122 | }); | ||
| 123 | |||
| 124 | let currentArtifact = input.artifact; | 121 | let currentArtifact = input.artifact; |
| 125 | let currentConfig = input.config; | 122 | let currentConfig = input.config; |
| 126 | 123 | ||
| @@ -186,13 +183,13 @@ export const createPersistedMcpAuthProvider = (input: { · 59 unmodified lines | |||
| 186 | saveTokens: (tokens) => | 183 | saveTokens: (tokens) => |
| 187 | Effect.runPromise(Effect.gen(function* () { | 184 | Effect.runPromise(Effect.gen(function* () { |
| 188 | const previousConfig = currentConfig; | 185 | const previousConfig = currentConfig; |
| 189 | const accessTokenRef = yield* storeSecretMaterial({ | 186 | const accessTokenRef = yield* input.storeSecretMaterial({ |
| 190 | purpose: "oauth_access_token", | 187 | purpose: "oauth_access_token", |
| 191 | value: tokens.access_token, | 188 | value: tokens.access_token, |
| 192 | name: `${currentArtifact.sourceId} MCP Access Token`, | 189 | name: `${currentArtifact.sourceId} MCP Access Token`, |
| 193 | }); | 190 | }); |
| 194 | const refreshTokenRef = tokens.refresh_token | 191 | const refreshTokenRef = tokens.refresh_token |
| 195 | ? yield* storeSecretMaterial({ | 192 | ? yield* input.storeSecretMaterial({ |
| 196 | purpose: "oauth_refresh_token", | 193 | purpose: "oauth_refresh_token", |
| 197 | value: tokens.refresh_token, | 194 | value: tokens.refresh_token, |
| 198 | name: `${currentArtifact.sourceId} MCP Refresh Token`, | 195 | name: `${currentArtifact.sourceId} MCP Refresh Token`, |
| @@ -212,7 +209,8 @@ export const createPersistedMcpAuthProvider = (input: { · 13 unmodified lines | |||
| 212 | }; | 209 | }; |
| 213 | 210 | ||
| 214 | yield* persistConfig(nextConfig); | 211 | yield* persistConfig(nextConfig); |
| 215 | yield* cleanupReplacedSecretRefs(input.rows, { | 212 | yield* cleanupReplacedSecretRefs({ |
| 213 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 216 | previousAccessToken: previousConfig.accessToken, | 214 | previousAccessToken: previousConfig.accessToken, |
| 217 | nextAccessToken: accessTokenRef, | 215 | nextAccessToken: accessTokenRef, |
| 218 | previousRefreshToken: previousConfig.refreshToken, | 216 | previousRefreshToken: previousConfig.refreshToken, |
| @@ -9,7 +9,9 @@ import { · 8 unmodified lines | |||
| 9 | import * as Effect from "effect/Effect"; | 9 | import * as Effect from "effect/Effect"; |
| 10 | import * as Option from "effect/Option"; | 10 | import * as Option from "effect/Option"; |
| 11 | 11 | ||
| 12 | import { createDefaultSecretMaterialDeleter } from "../local/secret-material-providers"; | 12 | import { |
| 13 | type DeleteSecretMaterial, | ||
| 14 | } from "../workspace/secret-material-providers"; | ||
| 13 | import type { ControlPlaneStoreShape } from "../store"; | 15 | import type { ControlPlaneStoreShape } from "../store"; |
| 14 | 16 | ||
| 15 | const providerGrantRefFromArtifact = ( | 17 | const providerGrantRefFromArtifact = ( |
| @@ -81,11 +83,8 @@ export const markProviderGrantOrphanedIfUnused = (rows: ControlPlaneStoreShape, · 65 unmodified lines | |||
| 81 | 83 | ||
| 82 | export const removeProviderAuthGrantSecret = (rows: ControlPlaneStoreShape, input: { | 84 | export const removeProviderAuthGrantSecret = (rows: ControlPlaneStoreShape, input: { |
| 83 | grant: ProviderAuthGrant; | 85 | grant: ProviderAuthGrant; |
| 84 | }): Effect.Effect<void, Error, never> => | 86 | }, deleteSecretMaterial: DeleteSecretMaterial): Effect.Effect<void, Error, never> => |
| 85 | Effect.gen(function* () { | 87 | Effect.gen(function* () { |
| 86 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | ||
| 87 | rows, | ||
| 88 | }); | ||
| 89 | yield* deleteSecretMaterial(input.grant.refreshToken).pipe( | 88 | yield* deleteSecretMaterial(input.grant.refreshToken).pipe( |
| 90 | Effect.either, | 89 | Effect.either, |
| 91 | Effect.ignore, | 90 | Effect.ignore, |
| @@ -17,10 +17,16 @@ import { · 16 unmodified lines | |||
| 17 | } from "./auth-artifacts"; | 17 | } from "./auth-artifacts"; |
| 18 | import { resolveAuthArtifactMaterialWithLeases } from "./auth-leases"; | 18 | import { resolveAuthArtifactMaterialWithLeases } from "./auth-leases"; |
| 19 | import type { | 19 | import type { |
| 20 | DeleteSecretMaterial, | ||
| 20 | ResolveSecretMaterial, | 21 | ResolveSecretMaterial, |
| 21 | SecretMaterialResolveContext, | 22 | SecretMaterialResolveContext, |
| 22 | } from "../local/secret-material-providers"; | 23 | StoreSecretMaterial, |
| 23 | import { SecretMaterialResolverService } from "../local/secret-material-providers"; | 24 | } from "../workspace/secret-material-providers"; |
| 25 | import { | ||
| 26 | SecretMaterialDeleterService, | ||
| 27 | SecretMaterialResolverService, | ||
| 28 | SecretMaterialStorerService, | ||
| 29 | } from "../workspace/secret-material-providers"; | ||
| 24 | import { runtimeEffectError } from "../effect-errors"; | 30 | import { runtimeEffectError } from "../effect-errors"; |
| 25 | 31 | ||
| 26 | const authForSlot = (input: { | 32 | const authForSlot = (input: { |
| @@ -61,6 +67,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 34 unmodified lines | |||
| 61 | actorAccountId?: AccountId | null; | 67 | actorAccountId?: AccountId | null; |
| 62 | rows?: ControlPlaneStoreShape; | 68 | rows?: ControlPlaneStoreShape; |
| 63 | resolveSecretMaterial: ResolveSecretMaterial; | 69 | resolveSecretMaterial: ResolveSecretMaterial; |
| 70 | storeSecretMaterial?: StoreSecretMaterial; | ||
| 71 | deleteSecretMaterial?: DeleteSecretMaterial; | ||
| 64 | context?: SecretMaterialResolveContext; | 72 | context?: SecretMaterialResolveContext; |
| 65 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => | 73 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => |
| 66 | Effect.gen(function* () { | 74 | Effect.gen(function* () { |
| @@ -85,6 +93,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 18 unmodified lines | |||
| 85 | rows: input.rows, | 93 | rows: input.rows, |
| 86 | artifact: artifactOption.value, | 94 | artifact: artifactOption.value, |
| 87 | resolveSecretMaterial: input.resolveSecretMaterial, | 95 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 96 | storeSecretMaterial: input.storeSecretMaterial ?? (() => Effect.dieMessage("storeSecretMaterial unavailable")), | ||
| 97 | deleteSecretMaterial: input.deleteSecretMaterial ?? (() => Effect.dieMessage("deleteSecretMaterial unavailable")), | ||
| 88 | context: input.context, | 98 | context: input.context, |
| 89 | }); | 99 | }); |
| 90 | } | 100 | } |
| @@ -106,6 +116,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 15 unmodified lines | |||
| 106 | rows: input.rows, | 116 | rows: input.rows, |
| 107 | artifact, | 117 | artifact, |
| 108 | resolveSecretMaterial: input.resolveSecretMaterial, | 118 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 119 | storeSecretMaterial: input.storeSecretMaterial ?? (() => Effect.dieMessage("storeSecretMaterial unavailable")), | ||
| 120 | deleteSecretMaterial: input.deleteSecretMaterial ?? (() => Effect.dieMessage("deleteSecretMaterial unavailable")), | ||
| 109 | context: input.context, | 121 | context: input.context, |
| 110 | }); | 122 | }); |
| 111 | } | 123 | } |
| @@ -138,6 +150,8 @@ export const RuntimeSourceAuthMaterialLive = Layer.effect( · 26 unmodified lines | |||
| 138 | Effect.gen(function* () { | 150 | Effect.gen(function* () { |
| 139 | const rows = yield* ControlPlaneStore; | 151 | const rows = yield* ControlPlaneStore; |
| 140 | const resolveSecretMaterial = yield* SecretMaterialResolverService; | 152 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 153 | const storeSecretMaterial = yield* SecretMaterialStorerService; | ||
| 154 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 141 | 155 | ||
| 142 | return RuntimeSourceAuthMaterialService.of({ | 156 | return RuntimeSourceAuthMaterialService.of({ |
| 143 | resolve: (input) => | 157 | resolve: (input) => |
| @@ -145,6 +159,8 @@ export const RuntimeSourceAuthMaterialLive = Layer.effect( · 1 unmodified line | |||
| 145 | ...input, | 159 | ...input, |
| 146 | rows, | 160 | rows, |
| 147 | resolveSecretMaterial, | 161 | resolveSecretMaterial, |
| 162 | storeSecretMaterial, | ||
| 163 | deleteSecretMaterial, | ||
| 148 | }), | 164 | }), |
| 149 | }); | 165 | }); |
| 150 | }), | 166 | }), |
| @@ -5,8 +5,8 @@ import type { · 4 unmodified lines | |||
| 5 | } from "#schema"; | 5 | } from "#schema"; |
| 6 | import * as Effect from "effect/Effect"; | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | ||
| 8 | import { RuntimeLocalWorkspaceService } from "../../local/runtime-context"; | 8 | import { RuntimeLocalWorkspaceService } from "../../workspace/runtime-context"; |
| 9 | import { SourceArtifactStore } from "../../local/storage"; | 9 | import { SourceArtifactStore } from "../../workspace/storage"; |
| 10 | import { getSourceAdapterForSource } from "../../sources/source-adapters"; | 10 | import { getSourceAdapterForSource } from "../../sources/source-adapters"; |
| 11 | import { RuntimeSourceStoreService } from "../../sources/source-store"; | 11 | import { RuntimeSourceStoreService } from "../../sources/source-store"; |
| 12 | import { RuntimeSourceCatalogSyncService } from "./sync"; | 12 | import { RuntimeSourceCatalogSyncService } from "./sync"; |
| @@ -42,7 +42,6 @@ export const reconcileMissingSourceCatalogArtifacts = (input: { · 29 unmodified lines | |||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | const artifact = yield* sourceArtifactStore.read({ | 44 | const artifact = yield* sourceArtifactStore.read({ |
| 45 | context: runtimeLocalWorkspace.context, | ||
| 46 | sourceId: source.id, | 45 | sourceId: source.id, |
| 47 | }); | 46 | }); |
| 48 | if (artifact !== null) { | 47 | if (artifact !== null) { |
| @@ -27,7 +27,7 @@ import type { · 26 unmodified lines | |||
| 27 | Executable, | 27 | Executable, |
| 28 | ShapeSymbol, | 28 | ShapeSymbol, |
| 29 | } from "@executor/ir/model"; | 29 | } from "@executor/ir/model"; |
| 30 | import { LocalSourceArtifactMissingError } from "../../local/errors"; | 30 | import { LocalSourceArtifactMissingError } from "../../workspace-errors"; |
| 31 | import { | 31 | import { |
| 32 | createCatalogTypeProjector, | 32 | createCatalogTypeProjector, |
| 33 | documentationComment, | 33 | documentationComment, |
| @@ -40,12 +40,12 @@ import { formatWithPrettier } from "../prettier-format"; · 6 unmodified lines | |||
| 40 | import { | 40 | import { |
| 41 | RuntimeLocalWorkspaceService, | 41 | RuntimeLocalWorkspaceService, |
| 42 | type RuntimeLocalWorkspaceState, | 42 | type RuntimeLocalWorkspaceState, |
| 43 | } from "../../local/runtime-context"; | 43 | } from "../../workspace/runtime-context"; |
| 44 | import type { LocalSourceArtifact } from "../../local/source-artifacts"; | 44 | import type { LocalSourceArtifact } from "../../source-artifacts"; |
| 45 | import { | 45 | import { |
| 46 | SourceArtifactStore, | 46 | SourceArtifactStore, |
| 47 | type SourceArtifactStoreShape, | 47 | type SourceArtifactStoreShape, |
| 48 | } from "../../local/storage"; | 48 | } from "../../workspace/storage"; |
| 49 | import { | 49 | import { |
| 50 | RuntimeSourceStoreService, | 50 | RuntimeSourceStoreService, |
| 51 | type RuntimeSourceStore, | 51 | type RuntimeSourceStore, |
| @@ -704,17 +704,16 @@ type SourceCatalogRuntimeServices = · 652 unmodified lines | |||
| 704 | const ensureRuntimeCatalogWorkspace = ( | 704 | const ensureRuntimeCatalogWorkspace = ( |
| 705 | deps: RuntimeSourceCatalogStoreDeps, | 705 | deps: RuntimeSourceCatalogStoreDeps, |
| 706 | workspaceId: WorkspaceId, | 706 | workspaceId: WorkspaceId, |
| 707 | ) => { | 707 | ) => |
| 708 | Effect.gen(function* () { | ||
| 708 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { | 709 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { |
| 709 | return Effect.fail( | 710 | return yield* Effect.fail( |
| 710 | runtimeEffectError("catalog/source/runtime", | 711 | runtimeEffectError("catalog/source/runtime", |
| 711 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, | 712 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, |
| 712 | ), | 713 | ), |
| 713 | ); | 714 | ); |
| 714 | } | 715 | } |
| 715 | 716 | }); | |
| 716 | return Effect.succeed(deps.runtimeLocalWorkspace.context); | ||
| 717 | }; | ||
| 718 | 717 | ||
| 719 | const buildSnapshotFromArtifact = (input: { | 718 | const buildSnapshotFromArtifact = (input: { |
| 720 | source: Source; | 719 | source: Source; |
| @@ -728,10 +727,7 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 7 unmodified lines | |||
| 728 | actorAccountId?: AccountId | null; | 727 | actorAccountId?: AccountId | null; |
| 729 | }): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => | 728 | }): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => |
| 730 | Effect.gen(function* () { | 729 | Effect.gen(function* () { |
| 731 | const workspaceContext = yield* ensureRuntimeCatalogWorkspace( | 730 | yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId); |
| 732 | deps, | ||
| 733 | input.workspaceId, | ||
| 734 | ); | ||
| 735 | const sources = yield* deps.sourceStore.loadSourcesInWorkspace( | 731 | const sources = yield* deps.sourceStore.loadSourcesInWorkspace( |
| 736 | input.workspaceId, | 732 | input.workspaceId, |
| 737 | { | 733 | { |
| @@ -742,7 +738,6 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 4 unmodified lines | |||
| 742 | const localCatalogs = yield* Effect.forEach(sources, (source) => | 738 | const localCatalogs = yield* Effect.forEach(sources, (source) => |
| 743 | Effect.gen(function* () { | 739 | Effect.gen(function* () { |
| 744 | const artifact = yield* deps.sourceArtifactStore.read({ | 740 | const artifact = yield* deps.sourceArtifactStore.read({ |
| 745 | context: workspaceContext, | ||
| 746 | sourceId: source.id, | 741 | sourceId: source.id, |
| 747 | }); | 742 | }); |
| 748 | if (artifact === null) { | 743 | if (artifact === null) { |
| @@ -783,17 +778,13 @@ const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, inpu · 34 unmodified lines | |||
| 783 | actorAccountId?: AccountId | null; | 778 | actorAccountId?: AccountId | null; |
| 784 | }): Effect.Effect<LoadedSourceCatalog, Error | LocalSourceArtifactMissingError, never> => | 779 | }): Effect.Effect<LoadedSourceCatalog, Error | LocalSourceArtifactMissingError, never> => |
| 785 | Effect.gen(function* () { | 780 | Effect.gen(function* () { |
| 786 | const workspaceContext = yield* ensureRuntimeCatalogWorkspace( | 781 | yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId); |
| 787 | deps, | ||
| 788 | input.workspaceId, | ||
| 789 | ); | ||
| 790 | const source = yield* deps.sourceStore.loadSourceById({ | 782 | const source = yield* deps.sourceStore.loadSourceById({ |
| 791 | workspaceId: input.workspaceId, | 783 | workspaceId: input.workspaceId, |
| 792 | sourceId: input.sourceId, | 784 | sourceId: input.sourceId, |
| 793 | actorAccountId: input.actorAccountId, | 785 | actorAccountId: input.actorAccountId, |
| 794 | }); | 786 | }); |
| 795 | const artifact = yield* deps.sourceArtifactStore.read({ | 787 | const artifact = yield* deps.sourceArtifactStore.read({ |
| 796 | context: workspaceContext, | ||
| 797 | sourceId: source.id, | 788 | sourceId: source.id, |
| 798 | }); | 789 | }); |
| 799 | if (artifact === null) { | 790 | if (artifact === null) { |
| @@ -11,16 +11,16 @@ import * as Layer from "effect/Layer"; · 10 unmodified lines | |||
| 11 | import { | 11 | import { |
| 12 | RuntimeLocalWorkspaceService, | 12 | RuntimeLocalWorkspaceService, |
| 13 | type RuntimeLocalWorkspaceState, | 13 | type RuntimeLocalWorkspaceState, |
| 14 | } from "../../local/runtime-context"; | 14 | } from "../../workspace/runtime-context"; |
| 15 | import { | 15 | import { |
| 16 | SourceArtifactStore, | 16 | SourceArtifactStore, |
| 17 | type SourceArtifactStoreShape, | 17 | type SourceArtifactStoreShape, |
| 18 | WorkspaceStateStore, | 18 | WorkspaceStateStore, |
| 19 | type WorkspaceStateStoreShape, | 19 | type WorkspaceStateStoreShape, |
| 20 | } from "../../local/storage"; | 20 | } from "../../workspace/storage"; |
| 21 | import { | 21 | import { |
| 22 | type LocalWorkspaceState, | 22 | type LocalWorkspaceState, |
| 23 | } from "../../local/workspace-state"; | 23 | } from "../../workspace-state"; |
| 24 | import { | 24 | import { |
| 25 | RuntimeSourceAuthMaterialService, | 25 | RuntimeSourceAuthMaterialService, |
| 26 | } from "../../auth/source-auth-material"; | 26 | } from "../../auth/source-auth-material"; |
| @@ -30,10 +30,11 @@ import { · 3 unmodified lines | |||
| 30 | import { | 30 | import { |
| 31 | catalogSyncResultFromMcpManifest, | 31 | catalogSyncResultFromMcpManifest, |
| 32 | } from "@executor/source-mcp"; | 32 | } from "@executor/source-mcp"; |
| 33 | import { SecretMaterialResolverService } from "../../local/secret-material-providers"; | 33 | import { SecretMaterialResolverService } from "../../workspace/secret-material-providers"; |
| 34 | import { snapshotFromSourceCatalogSyncResult } from "@executor/source-core"; | 34 | import { snapshotFromSourceCatalogSyncResult } from "@executor/source-core"; |
| 35 | import { | 35 | import { |
| 36 | refreshSourceTypeDeclarationInBackground, | 36 | SourceTypeDeclarationsRefresherService, |
| 37 | type SourceTypeDeclarationsRefresherShape, | ||
| 37 | } from "./type-declarations"; | 38 | } from "./type-declarations"; |
| 38 | import { runtimeEffectError } from "../../effect-errors"; | 39 | import { runtimeEffectError } from "../../effect-errors"; |
| 39 | 40 | ||
| @@ -46,6 +47,7 @@ type RuntimeSourceCatalogSyncDeps = { · 6 unmodified lines | |||
| 46 | runtimeLocalWorkspace: RuntimeLocalWorkspaceState; | 47 | runtimeLocalWorkspace: RuntimeLocalWorkspaceState; |
| 47 | workspaceStateStore: WorkspaceStateStoreShape; | 48 | workspaceStateStore: WorkspaceStateStoreShape; |
| 48 | sourceArtifactStore: SourceArtifactStoreShape; | 49 | sourceArtifactStore: SourceArtifactStoreShape; |
| 50 | sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape; | ||
| 49 | resolveSecretMaterial: Effect.Effect.Success<typeof SecretMaterialResolverService>; | 51 | resolveSecretMaterial: Effect.Effect.Success<typeof SecretMaterialResolverService>; |
| 50 | sourceAuthMaterialService: Effect.Effect.Success<typeof RuntimeSourceAuthMaterialService>; | 52 | sourceAuthMaterialService: Effect.Effect.Success<typeof RuntimeSourceAuthMaterialService>; |
| 51 | }; | 53 | }; |
| @@ -54,6 +56,7 @@ type SourceCatalogSyncServices = · 2 unmodified lines | |||
| 54 | | RuntimeLocalWorkspaceService | 56 | | RuntimeLocalWorkspaceService |
| 55 | | WorkspaceStateStore | 57 | | WorkspaceStateStore |
| 56 | | SourceArtifactStore | 58 | | SourceArtifactStore |
| 59 | | SourceTypeDeclarationsRefresherService | ||
| 57 | | RuntimeSourceAuthMaterialService | 60 | | RuntimeSourceAuthMaterialService |
| 58 | | SecretMaterialResolverService; | 61 | | SecretMaterialResolverService; |
| 59 | 62 | ||
| @@ -75,17 +78,16 @@ export class RuntimeSourceCatalogSyncService extends Context.Tag( · 15 unmodified lines | |||
| 75 | const ensureRuntimeCatalogSyncWorkspace = ( | 78 | const ensureRuntimeCatalogSyncWorkspace = ( |
| 76 | deps: RuntimeSourceCatalogSyncDeps, | 79 | deps: RuntimeSourceCatalogSyncDeps, |
| 77 | workspaceId: Source["workspaceId"], | 80 | workspaceId: Source["workspaceId"], |
| 78 | ) => { | 81 | ) => |
| 82 | Effect.gen(function* () { | ||
| 79 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { | 83 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { |
| 80 | return Effect.fail( | 84 | return yield* Effect.fail( |
| 81 | runtimeEffectError("catalog/source/sync", | 85 | runtimeEffectError("catalog/source/sync", |
| 82 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, | 86 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, |
| 83 | ), | 87 | ), |
| 84 | ); | 88 | ); |
| 85 | } | 89 | } |
| 86 | 90 | }); | |
| 87 | return Effect.succeed(deps.runtimeLocalWorkspace.context); | ||
| 88 | }; | ||
| 89 | 91 | ||
| 90 | const syncSourceCatalogWithDeps = ( | 92 | const syncSourceCatalogWithDeps = ( |
| 91 | deps: RuntimeSourceCatalogSyncDeps, | 93 | deps: RuntimeSourceCatalogSyncDeps, |
| @@ -95,13 +97,10 @@ const syncSourceCatalogWithDeps = ( · 3 unmodified lines | |||
| 95 | }, | 97 | }, |
| 96 | ): Effect.Effect<void, Error, never> => | 98 | ): Effect.Effect<void, Error, never> => |
| 97 | Effect.gen(function* () { | 99 | Effect.gen(function* () { |
| 98 | const workspaceContext = yield* ensureRuntimeCatalogSyncWorkspace( | 100 | yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId); |
| 99 | deps, | ||
| 100 | input.source.workspaceId, | ||
| 101 | ); | ||
| 102 | 101 | ||
| 103 | if (!shouldIndexSource(input.source)) { | 102 | if (!shouldIndexSource(input.source)) { |
| 104 | const state = yield* deps.workspaceStateStore.load(workspaceContext); | 103 | const state = yield* deps.workspaceStateStore.load(); |
| 105 | const existingSourceState = state.sources[input.source.id]; | 104 | const existingSourceState = state.sources[input.source.id]; |
| 106 | const nextState: LocalWorkspaceState = { | 105 | const nextState: LocalWorkspaceState = { |
| 107 | ...state, | 106 | ...state, |
| @@ -117,15 +116,11 @@ const syncSourceCatalogWithDeps = ( · 9 unmodified lines | |||
| 117 | }, | 116 | }, |
| 118 | }; | 117 | }; |
| 119 | yield* deps.workspaceStateStore.write({ | 118 | yield* deps.workspaceStateStore.write({ |
| 120 | context: workspaceContext, | ||
| 121 | state: nextState, | 119 | state: nextState, |
| 122 | }); | 120 | }); |
| 123 | yield* Effect.sync(() => { | 121 | yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ |
| 124 | refreshSourceTypeDeclarationInBackground({ | 122 | source: input.source, |
| 125 | context: workspaceContext, | 123 | snapshot: null, |
| 126 | source: input.source, | ||
| 127 | snapshot: null, | ||
| 128 | }); | ||
| 129 | }); | 124 | }); |
| 130 | return; | 125 | return; |
| 131 | } | 126 | } |
| @@ -143,7 +138,6 @@ const syncSourceCatalogWithDeps = ( · 11 unmodified lines | |||
| 143 | }); | 138 | }); |
| 144 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); | 139 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); |
| 145 | yield* deps.sourceArtifactStore.write({ | 140 | yield* deps.sourceArtifactStore.write({ |
| 146 | context: workspaceContext, | ||
| 147 | sourceId: input.source.id, | 141 | sourceId: input.source.id, |
| 148 | artifact: deps.sourceArtifactStore.build({ | 142 | artifact: deps.sourceArtifactStore.build({ |
| 149 | source: input.source, | 143 | source: input.source, |
| @@ -151,7 +145,7 @@ const syncSourceCatalogWithDeps = ( · 1 unmodified line | |||
| 151 | }), | 145 | }), |
| 152 | }); | 146 | }); |
| 153 | 147 | ||
| 154 | const state = yield* deps.workspaceStateStore.load(workspaceContext); | 148 | const state = yield* deps.workspaceStateStore.load(); |
| 155 | const existingSourceState = state.sources[input.source.id]; | 149 | const existingSourceState = state.sources[input.source.id]; |
| 156 | const nextState: LocalWorkspaceState = { | 150 | const nextState: LocalWorkspaceState = { |
| 157 | ...state, | 151 | ...state, |
| @@ -167,16 +161,12 @@ const syncSourceCatalogWithDeps = ( · 9 unmodified lines | |||
| 167 | }, | 161 | }, |
| 168 | }; | 162 | }; |
| 169 | yield* deps.workspaceStateStore.write({ | 163 | yield* deps.workspaceStateStore.write({ |
| 170 | context: workspaceContext, | ||
| 171 | state: nextState, | 164 | state: nextState, |
| 172 | }); | 165 | }); |
| 173 | 166 | ||
| 174 | yield* Effect.sync(() => { | 167 | yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ |
| 175 | refreshSourceTypeDeclarationInBackground({ | 168 | source: input.source, |
| 176 | context: workspaceContext, | 169 | snapshot, |
| 177 | source: input.source, | ||
| 178 | snapshot, | ||
| 179 | }); | ||
| 180 | }); | 170 | }); |
| 181 | }).pipe( | 171 | }).pipe( |
| 182 | Effect.withSpan("source.catalog.sync", { | 172 | Effect.withSpan("source.catalog.sync", { |
| @@ -197,10 +187,7 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 14 unmodified lines | |||
| 197 | }, | 187 | }, |
| 198 | ): Effect.Effect<void, Error, never> => | 188 | ): Effect.Effect<void, Error, never> => |
| 199 | Effect.gen(function* () { | 189 | Effect.gen(function* () { |
| 200 | const workspaceContext = yield* ensureRuntimeCatalogSyncWorkspace( | 190 | yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId); |
| 201 | deps, | ||
| 202 | input.source.workspaceId, | ||
| 203 | ); | ||
| 204 | const syncResult = catalogSyncResultFromMcpManifest({ | 191 | const syncResult = catalogSyncResultFromMcpManifest({ |
| 205 | source: input.source, | 192 | source: input.source, |
| 206 | endpoint: input.source.endpoint, | 193 | endpoint: input.source.endpoint, |
| @@ -209,7 +196,6 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 2 unmodified lines | |||
| 209 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); | 196 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); |
| 210 | 197 | ||
| 211 | yield* deps.sourceArtifactStore.write({ | 198 | yield* deps.sourceArtifactStore.write({ |
| 212 | context: workspaceContext, | ||
| 213 | sourceId: input.source.id, | 199 | sourceId: input.source.id, |
| 214 | artifact: deps.sourceArtifactStore.build({ | 200 | artifact: deps.sourceArtifactStore.build({ |
| 215 | source: input.source, | 201 | source: input.source, |
| @@ -217,12 +203,9 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 1 unmodified line | |||
| 217 | }), | 203 | }), |
| 218 | }); | 204 | }); |
| 219 | 205 | ||
| 220 | yield* Effect.sync(() => { | 206 | yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ |
| 221 | refreshSourceTypeDeclarationInBackground({ | 207 | source: input.source, |
| 222 | context: workspaceContext, | 208 | snapshot, |
| 223 | source: input.source, | ||
| 224 | snapshot, | ||
| 225 | }); | ||
| 226 | }); | 209 | }); |
| 227 | }); | 210 | }); |
| 228 | 211 | ||
| @@ -234,6 +217,8 @@ export const syncSourceCatalog = (input: { · 5 unmodified lines | |||
| 234 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; | 217 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 235 | const workspaceStateStore = yield* WorkspaceStateStore; | 218 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 236 | const sourceArtifactStore = yield* SourceArtifactStore; | 219 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 220 | const sourceTypeDeclarationsRefresher = | ||
| 221 | yield* SourceTypeDeclarationsRefresherService; | ||
| 237 | const resolveSecretMaterial = yield* SecretMaterialResolverService; | 222 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 238 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; | 223 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 239 | 224 | ||
| @@ -242,6 +227,7 @@ export const syncSourceCatalog = (input: { · 2 unmodified lines | |||
| 242 | runtimeLocalWorkspace, | 227 | runtimeLocalWorkspace, |
| 243 | workspaceStateStore, | 228 | workspaceStateStore, |
| 244 | sourceArtifactStore, | 229 | sourceArtifactStore, |
| 230 | sourceTypeDeclarationsRefresher, | ||
| 245 | resolveSecretMaterial, | 231 | resolveSecretMaterial, |
| 246 | sourceAuthMaterialService, | 232 | sourceAuthMaterialService, |
| 247 | }, | 233 | }, |
| @@ -260,6 +246,8 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 12 unmodified lines | |||
| 260 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; | 246 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 261 | const workspaceStateStore = yield* WorkspaceStateStore; | 247 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 262 | const sourceArtifactStore = yield* SourceArtifactStore; | 248 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 249 | const sourceTypeDeclarationsRefresher = | ||
| 250 | yield* SourceTypeDeclarationsRefresherService; | ||
| 263 | const resolveSecretMaterial = yield* SecretMaterialResolverService; | 251 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 264 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; | 252 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 265 | 253 | ||
| @@ -268,6 +256,7 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 2 unmodified lines | |||
| 268 | runtimeLocalWorkspace, | 256 | runtimeLocalWorkspace, |
| 269 | workspaceStateStore, | 257 | workspaceStateStore, |
| 270 | sourceArtifactStore, | 258 | sourceArtifactStore, |
| 259 | sourceTypeDeclarationsRefresher, | ||
| 271 | resolveSecretMaterial, | 260 | resolveSecretMaterial, |
| 272 | sourceAuthMaterialService, | 261 | sourceAuthMaterialService, |
| 273 | }, | 262 | }, |
| @@ -281,6 +270,8 @@ export const RuntimeSourceCatalogSyncLive = Layer.effect( · 7 unmodified lines | |||
| 281 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; | 270 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 282 | const workspaceStateStore = yield* WorkspaceStateStore; | 271 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 283 | const sourceArtifactStore = yield* SourceArtifactStore; | 272 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 273 | const sourceTypeDeclarationsRefresher = | ||
| 274 | yield* SourceTypeDeclarationsRefresherService; | ||
| 284 | const resolveSecretMaterial = yield* SecretMaterialResolverService; | 275 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 285 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; | 276 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 286 | 277 | ||
| @@ -288,6 +279,7 @@ export const RuntimeSourceCatalogSyncLive = Layer.effect( · 1 unmodified line | |||
| 288 | runtimeLocalWorkspace, | 279 | runtimeLocalWorkspace, |
| 289 | workspaceStateStore, | 280 | workspaceStateStore, |
| 290 | sourceArtifactStore, | 281 | sourceArtifactStore, |
| 282 | sourceTypeDeclarationsRefresher, | ||
| 291 | resolveSecretMaterial, | 283 | resolveSecretMaterial, |
| 292 | sourceAuthMaterialService, | 284 | sourceAuthMaterialService, |
| 293 | }; | 285 | }; |
| @@ -1,427 +1,23 @@ | |||
| 1 | import { join } from "node:path"; | ||
| 2 | import { FileSystem } from "@effect/platform"; | ||
| 3 | import { NodeFileSystem } from "@effect/platform-node"; | ||
| 4 | import type { Source } from "#schema"; | 1 | import type { Source } from "#schema"; |
| 5 | import * as Cause from "effect/Cause"; | ||
| 6 | import * as Effect from "effect/Effect"; | ||
| 7 | import * as Fiber from "effect/Fiber"; | ||
| 8 | |||
| 9 | import { projectCatalogForAgentSdk } from "@executor/ir/catalog"; | ||
| 10 | import type { CatalogSnapshotV1 } from "@executor/ir/model"; | 2 | import type { CatalogSnapshotV1 } from "@executor/ir/model"; |
| 11 | import { | 3 | import * as Context from "effect/Context"; |
| 12 | createCatalogTypeProjector, | 4 | import * as Effect from "effect/Effect"; |
| 13 | formatPropertyKey, | ||
| 14 | joinTypeNameSegments, | ||
| 15 | projectedCatalogTypeRoots, | ||
| 16 | shapeAllowsOmittedArgs, | ||
| 17 | } from "../catalog-typescript"; | ||
| 18 | import type { ResolvedLocalWorkspaceContext } from "../../local/config"; | ||
| 19 | import { | ||
| 20 | LocalFileSystemError, | ||
| 21 | unknownLocalErrorDetails, | ||
| 22 | } from "../../local/errors"; | ||
| 23 | 5 | ||
| 24 | type SourceDeclarationEntry = { | 6 | export type BoundSourceDeclarationEntry = { |
| 25 | source: Source; | 7 | source: Source; |
| 26 | snapshot: CatalogSnapshotV1; | 8 | snapshot: CatalogSnapshotV1; |
| 27 | }; | 9 | }; |
| 28 | 10 | ||
| 29 | type ToolMethodNode = { | 11 | export type SourceTypeDeclarationsRefresherShape = { |
| 30 | readonly segments: readonly string[]; | 12 | refreshWorkspaceInBackground: (input: { |
| 31 | readonly inputType: string; | 13 | entries: readonly BoundSourceDeclarationEntry[]; |
| 32 | readonly outputType: string; | 14 | }) => Effect.Effect<void, never, never>; |
| 33 | readonly argsOptional: boolean; | 15 | refreshSourceInBackground: (input: { |
| 34 | }; | 16 | source: Source; |
| 35 | 17 | snapshot: CatalogSnapshotV1 | null; | |
| 36 | type SourceDeclarationModel = { | 18 | }) => Effect.Effect<void, never, never>; |
| 37 | readonly methods: readonly ToolMethodNode[]; | ||
| 38 | readonly supportingTypes: readonly string[]; | ||
| 39 | }; | ||
| 40 | |||
| 41 | type ToolTreeNode = { | ||
| 42 | method: ToolMethodNode | null; | ||
| 43 | children: Map<string, ToolTreeNode>; | ||
| 44 | }; | ||
| 45 | |||
| 46 | type SourceDeclarationStub = { | ||
| 47 | sourceId: string; | ||
| 48 | }; | ||
| 49 | |||
| 50 | const GENERATED_TYPES_DIRECTORY = "types"; | ||
| 51 | const GENERATED_SOURCE_TYPES_DIRECTORY = "sources"; | ||
| 52 | |||
| 53 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | ||
| 54 | new LocalFileSystemError({ | ||
| 55 | message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`, | ||
| 56 | action, | ||
| 57 | path, | ||
| 58 | details: unknownLocalErrorDetails(cause), | ||
| 59 | }); | ||
| 60 | |||
| 61 | const declarationDirectory = (context: ResolvedLocalWorkspaceContext): string => | ||
| 62 | join(context.configDirectory, GENERATED_TYPES_DIRECTORY); | ||
| 63 | |||
| 64 | const sourceDeclarationDirectory = (context: ResolvedLocalWorkspaceContext): string => | ||
| 65 | join(declarationDirectory(context), GENERATED_SOURCE_TYPES_DIRECTORY); | ||
| 66 | |||
| 67 | const sourceDeclarationFileName = (sourceId: string): string => `${sourceId}.d.ts`; | ||
| 68 | |||
| 69 | const sourceDeclarationPath = (context: ResolvedLocalWorkspaceContext, sourceId: string): string => | ||
| 70 | join(sourceDeclarationDirectory(context), sourceDeclarationFileName(sourceId)); | ||
| 71 | |||
| 72 | const aggregateDeclarationPath = (context: ResolvedLocalWorkspaceContext): string => | ||
| 73 | join(declarationDirectory(context), "index.d.ts"); | ||
| 74 | |||
| 75 | const sourceTypeInterfaceName = (sourceId: string): string => | ||
| 76 | `SourceTools_${sourceId.replace(/[^A-Za-z0-9_$]+/g, "_")}`; | ||
| 77 | |||
| 78 | const methodSignature = (method: ToolMethodNode): string => | ||
| 79 | `(${method.argsOptional ? "args?:" : "args:"} ${method.inputType}) => Promise<${method.outputType}>`; | ||
| 80 | |||
| 81 | const createToolTreeNode = (): ToolTreeNode => ({ | ||
| 82 | method: null, | ||
| 83 | children: new Map(), | ||
| 84 | }); | ||
| 85 | |||
| 86 | const objectTypeLiteral = ( | ||
| 87 | lines: readonly string[], | ||
| 88 | indent: string, | ||
| 89 | ): string => { | ||
| 90 | if (lines.length === 0) { | ||
| 91 | return "{}"; | ||
| 92 | } | ||
| 93 | |||
| 94 | return [ | ||
| 95 | "{", | ||
| 96 | ...lines.map((line) => `${indent}${line}`), | ||
| 97 | `${indent.slice(0, -2)}}`, | ||
| 98 | ].join("\n"); | ||
| 99 | }; | ||
| 100 | |||
| 101 | const renderToolTreeType = (node: ToolTreeNode, indentLevel: number): string => { | ||
| 102 | const indent = " ".repeat(indentLevel + 1); | ||
| 103 | const childLines = [...node.children.entries()] | ||
| 104 | .sort(([left], [right]) => left.localeCompare(right)) | ||
| 105 | .map(([segment, child]) => | ||
| 106 | `${formatPropertyKey(segment)}: ${renderToolTreeType(child, indentLevel + 1)};` | ||
| 107 | ); | ||
| 108 | |||
| 109 | const objectType = objectTypeLiteral(childLines, indent); | ||
| 110 | if (node.method === null) { | ||
| 111 | return objectType; | ||
| 112 | } | ||
| 113 | |||
| 114 | const callable = methodSignature(node.method); | ||
| 115 | return node.children.size === 0 | ||
| 116 | ? callable | ||
| 117 | : `(${callable}) & ${objectType}`; | ||
| 118 | }; | ||
| 119 | |||
| 120 | const buildToolTree = (methods: readonly ToolMethodNode[]): ToolTreeNode => { | ||
| 121 | const root = createToolTreeNode(); | ||
| 122 | |||
| 123 | for (const method of methods) { | ||
| 124 | let current = root; | ||
| 125 | for (const segment of method.segments) { | ||
| 126 | const existing = current.children.get(segment); | ||
| 127 | if (existing) { | ||
| 128 | current = existing; | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | const next = createToolTreeNode(); | ||
| 133 | current.children.set(segment, next); | ||
| 134 | current = next; | ||
| 135 | } | ||
| 136 | |||
| 137 | current.method = method; | ||
| 138 | } | ||
| 139 | |||
| 140 | return root; | ||
| 141 | }; | ||
| 142 | |||
| 143 | const buildSourceDeclarationModel = (snapshot: CatalogSnapshotV1): SourceDeclarationModel => { | ||
| 144 | const projected = projectCatalogForAgentSdk({ | ||
| 145 | catalog: snapshot.catalog, | ||
| 146 | }); | ||
| 147 | const toolDescriptors = Object.values(projected.toolDescriptors) | ||
| 148 | .sort((left, right) => left.toolPath.join(".").localeCompare(right.toolPath.join("."))); | ||
| 149 | const renderer = createCatalogTypeProjector({ | ||
| 150 | catalog: projected.catalog, | ||
| 151 | roots: projectedCatalogTypeRoots(projected), | ||
| 152 | }); | ||
| 153 | |||
| 154 | const methods = toolDescriptors | ||
| 155 | .map((descriptor) => ({ | ||
| 156 | segments: descriptor.toolPath, | ||
| 157 | inputType: renderer.renderDeclarationShape(descriptor.callShapeId, { | ||
| 158 | aliasHint: joinTypeNameSegments(...descriptor.toolPath, "call"), | ||
| 159 | }), | ||
| 160 | outputType: descriptor.resultShapeId | ||
| 161 | ? renderer.renderDeclarationShape(descriptor.resultShapeId, { | ||
| 162 | aliasHint: joinTypeNameSegments(...descriptor.toolPath, "result"), | ||
| 163 | }) | ||
| 164 | : "unknown", | ||
| 165 | argsOptional: shapeAllowsOmittedArgs(projected.catalog, descriptor.callShapeId), | ||
| 166 | }) satisfies ToolMethodNode); | ||
| 167 | |||
| 168 | return { | ||
| 169 | methods, | ||
| 170 | supportingTypes: renderer.supportingDeclarations(), | ||
| 171 | }; | ||
| 172 | }; | ||
| 173 | |||
| 174 | const sourceDeclarationText = (entry: SourceDeclarationEntry): string => { | ||
| 175 | const interfaceName = sourceTypeInterfaceName(entry.source.id); | ||
| 176 | const declarationModel = buildSourceDeclarationModel(entry.snapshot); | ||
| 177 | const tree = buildToolTree(declarationModel.methods); | ||
| 178 | const body = renderToolTreeType(tree, 0); | ||
| 179 | |||
| 180 | return [ | ||
| 181 | "// Generated by executor. Do not edit by hand.", | ||
| 182 | `// Source: ${entry.source.name} (${entry.source.id})`, | ||
| 183 | "", | ||
| 184 | ...declarationModel.supportingTypes, | ||
| 185 | ...(declarationModel.supportingTypes.length > 0 ? [""] : []), | ||
| 186 | `export interface ${interfaceName} ${body}`, | ||
| 187 | "", | ||
| 188 | `export declare const tools: ${interfaceName};`, | ||
| 189 | `export type ${interfaceName}Tools = ${interfaceName};`, | ||
| 190 | "export default tools;", | ||
| 191 | "", | ||
| 192 | ].join("\n"); | ||
| 193 | }; | ||
| 194 | |||
| 195 | const aggregateDeclarationText = (entries: readonly SourceDeclarationEntry[]): string => { | ||
| 196 | return aggregateDeclarationTextFromSourceIds( | ||
| 197 | entries.map((entry) => ({ sourceId: entry.source.id })), | ||
| 198 | ); | ||
| 199 | }; | ||
| 200 | |||
| 201 | const aggregateDeclarationTextFromSourceIds = (entries: readonly SourceDeclarationStub[]): string => { | ||
| 202 | const sorted = [...entries].sort((left, right) => left.sourceId.localeCompare(right.sourceId)); | ||
| 203 | const imports = sorted.map((entry) => | ||
| 204 | `import type { ${sourceTypeInterfaceName(entry.sourceId)} } from "../sources/${entry.sourceId}";` | ||
| 205 | ); | ||
| 206 | const intersections = sorted.map((entry) => sourceTypeInterfaceName(entry.sourceId)); | ||
| 207 | const executorToolsType = intersections.length > 0 | ||
| 208 | ? intersections.join(" & ") | ||
| 209 | : "{}"; | ||
| 210 | |||
| 211 | return [ | ||
| 212 | "// Generated by executor. Do not edit by hand.", | ||
| 213 | ...imports, | ||
| 214 | ...(imports.length > 0 ? [""] : []), | ||
| 215 | `export type ExecutorSourceTools = ${executorToolsType};`, | ||
| 216 | "", | ||
| 217 | "declare global {", | ||
| 218 | " const tools: ExecutorSourceTools;", | ||
| 219 | "}", | ||
| 220 | "", | ||
| 221 | "export declare const tools: ExecutorSourceTools;", | ||
| 222 | "export default tools;", | ||
| 223 | "", | ||
| 224 | ].join("\n"); | ||
| 225 | }; | ||
| 226 | |||
| 227 | export const syncWorkspaceSourceTypeDeclarations = (input: { | ||
| 228 | context: ResolvedLocalWorkspaceContext; | ||
| 229 | entries: readonly SourceDeclarationEntry[]; | ||
| 230 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 231 | Effect.gen(function* () { | ||
| 232 | const fs = yield* FileSystem.FileSystem; | ||
| 233 | const declarationsDir = declarationDirectory(input.context); | ||
| 234 | const perSourceDir = sourceDeclarationDirectory(input.context); | ||
| 235 | const activeEntries = input.entries | ||
| 236 | .filter((entry) => entry.source.enabled && entry.source.status === "connected") | ||
| 237 | .sort((left, right) => left.source.id.localeCompare(right.source.id)); | ||
| 238 | |||
| 239 | yield* fs.makeDirectory(declarationsDir, { recursive: true }).pipe( | ||
| 240 | Effect.mapError(mapFileSystemError(declarationsDir, "create declaration directory")), | ||
| 241 | ); | ||
| 242 | yield* fs.makeDirectory(perSourceDir, { recursive: true }).pipe( | ||
| 243 | Effect.mapError(mapFileSystemError(perSourceDir, "create source declaration directory")), | ||
| 244 | ); | ||
| 245 | |||
| 246 | const expectedFiles = new Set( | ||
| 247 | activeEntries.map((entry) => sourceDeclarationFileName(entry.source.id)), | ||
| 248 | ); | ||
| 249 | const existingFiles = yield* fs.readDirectory(perSourceDir).pipe( | ||
| 250 | Effect.mapError(mapFileSystemError(perSourceDir, "read source declaration directory")), | ||
| 251 | ); | ||
| 252 | |||
| 253 | for (const existingFile of existingFiles) { | ||
| 254 | if (expectedFiles.has(existingFile)) { | ||
| 255 | continue; | ||
| 256 | } | ||
| 257 | |||
| 258 | const stalePath = join(perSourceDir, existingFile); | ||
| 259 | yield* fs.remove(stalePath).pipe( | ||
| 260 | Effect.mapError(mapFileSystemError(stalePath, "remove stale source declaration")), | ||
| 261 | ); | ||
| 262 | } | ||
| 263 | |||
| 264 | for (const entry of activeEntries) { | ||
| 265 | const filePath = sourceDeclarationPath(input.context, entry.source.id); | ||
| 266 | yield* fs.writeFileString(filePath, sourceDeclarationText(entry)).pipe( | ||
| 267 | Effect.mapError(mapFileSystemError(filePath, "write source declaration")), | ||
| 268 | ); | ||
| 269 | } | ||
| 270 | |||
| 271 | const aggregatePath = aggregateDeclarationPath(input.context); | ||
| 272 | yield* fs.writeFileString( | ||
| 273 | aggregatePath, | ||
| 274 | aggregateDeclarationText(activeEntries), | ||
| 275 | ).pipe( | ||
| 276 | Effect.mapError(mapFileSystemError(aggregatePath, "write aggregate declaration")), | ||
| 277 | ); | ||
| 278 | }); | ||
| 279 | |||
| 280 | export const syncSourceTypeDeclaration = (input: { | ||
| 281 | context: ResolvedLocalWorkspaceContext; | ||
| 282 | source: Source; | ||
| 283 | snapshot: CatalogSnapshotV1 | null; | ||
| 284 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 285 | Effect.gen(function* () { | ||
| 286 | const fs = yield* FileSystem.FileSystem; | ||
| 287 | const declarationsDir = declarationDirectory(input.context); | ||
| 288 | const perSourceDir = sourceDeclarationDirectory(input.context); | ||
| 289 | |||
| 290 | yield* fs.makeDirectory(declarationsDir, { recursive: true }).pipe( | ||
| 291 | Effect.mapError(mapFileSystemError(declarationsDir, "create declaration directory")), | ||
| 292 | ); | ||
| 293 | yield* fs.makeDirectory(perSourceDir, { recursive: true }).pipe( | ||
| 294 | Effect.mapError(mapFileSystemError(perSourceDir, "create source declaration directory")), | ||
| 295 | ); | ||
| 296 | |||
| 297 | const filePath = sourceDeclarationPath(input.context, input.source.id); | ||
| 298 | const shouldWrite = input.source.enabled | ||
| 299 | && input.source.status === "connected" | ||
| 300 | && input.snapshot !== null; | ||
| 301 | |||
| 302 | if (shouldWrite) { | ||
| 303 | const snapshot = input.snapshot; | ||
| 304 | if (snapshot === null) { | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | yield* fs.writeFileString( | ||
| 308 | filePath, | ||
| 309 | sourceDeclarationText({ | ||
| 310 | source: input.source, | ||
| 311 | snapshot, | ||
| 312 | }), | ||
| 313 | ).pipe( | ||
| 314 | Effect.mapError(mapFileSystemError(filePath, "write source declaration")), | ||
| 315 | ); | ||
| 316 | } else { | ||
| 317 | const exists = yield* fs.exists(filePath).pipe( | ||
| 318 | Effect.mapError(mapFileSystemError(filePath, "check source declaration path")), | ||
| 319 | ); | ||
| 320 | if (exists) { | ||
| 321 | yield* fs.remove(filePath).pipe( | ||
| 322 | Effect.mapError(mapFileSystemError(filePath, "remove source declaration")), | ||
| 323 | ); | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | const sourceIds = (yield* fs.readDirectory(perSourceDir).pipe( | ||
| 328 | Effect.mapError(mapFileSystemError(perSourceDir, "read source declaration directory")), | ||
| 329 | )) | ||
| 330 | .filter((fileName) => fileName.endsWith(".d.ts")) | ||
| 331 | .map((fileName) => ({ sourceId: fileName.slice(0, -".d.ts".length) })); | ||
| 332 | |||
| 333 | const aggregatePath = aggregateDeclarationPath(input.context); | ||
| 334 | yield* fs.writeFileString( | ||
| 335 | aggregatePath, | ||
| 336 | aggregateDeclarationTextFromSourceIds(sourceIds), | ||
| 337 | ).pipe( | ||
| 338 | Effect.mapError(mapFileSystemError(aggregatePath, "write aggregate declaration")), | ||
| 339 | ); | ||
| 340 | }); | ||
| 341 | |||
| 342 | export const syncWorkspaceSourceTypeDeclarationsNode = (input: { | ||
| 343 | context: ResolvedLocalWorkspaceContext; | ||
| 344 | entries: readonly SourceDeclarationEntry[]; | ||
| 345 | }): Effect.Effect<void, LocalFileSystemError, never> => | ||
| 346 | syncWorkspaceSourceTypeDeclarations(input).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 347 | |||
| 348 | export const syncSourceTypeDeclarationNode = (input: { | ||
| 349 | context: ResolvedLocalWorkspaceContext; | ||
| 350 | source: Source; | ||
| 351 | snapshot: CatalogSnapshotV1 | null; | ||
| 352 | }): Effect.Effect<void, LocalFileSystemError, never> => | ||
| 353 | syncSourceTypeDeclaration(input).pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 354 | |||
| 355 | const logBackgroundDeclarationError = (label: string, cause: unknown): void => { | ||
| 356 | const message = Cause.isCause(cause) | ||
| 357 | ? Cause.pretty(cause) | ||
| 358 | : cause instanceof Error | ||
| 359 | ? cause.message | ||
| 360 | : String(cause); | ||
| 361 | console.warn(`[source-types] ${label} failed: ${message}`); | ||
| 362 | }; | ||
| 363 | |||
| 364 | const BACKGROUND_DECLARATION_REFRESH_DELAY = "1500 millis"; | ||
| 365 | const workspaceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>(); | ||
| 366 | const sourceRefreshFibers = new Map<string, Fiber.RuntimeFiber<void, never>>(); | ||
| 367 | |||
| 368 | export const refreshWorkspaceSourceTypeDeclarationsInBackground = (input: { | ||
| 369 | context: ResolvedLocalWorkspaceContext; | ||
| 370 | entries: readonly SourceDeclarationEntry[]; | ||
| 371 | }): void => { | ||
| 372 | const key = input.context.configDirectory; | ||
| 373 | const existingFiber = workspaceRefreshFibers.get(key); | ||
| 374 | if (existingFiber) { | ||
| 375 | Effect.runFork(Fiber.interruptFork(existingFiber)); | ||
| 376 | } | ||
| 377 | |||
| 378 | const fiber = Effect.runFork( | ||
| 379 | Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe( | ||
| 380 | Effect.zipRight( | ||
| 381 | syncWorkspaceSourceTypeDeclarationsNode(input).pipe( | ||
| 382 | Effect.catchAllCause((cause) => | ||
| 383 | Effect.sync(() => { | ||
| 384 | logBackgroundDeclarationError("workspace declaration refresh", cause); | ||
| 385 | }) | ||
| 386 | ), | ||
| 387 | ), | ||
| 388 | ), | ||
| 389 | ), | ||
| 390 | ); | ||
| 391 | |||
| 392 | workspaceRefreshFibers.set(key, fiber); | ||
| 393 | fiber.addObserver(() => { | ||
| 394 | workspaceRefreshFibers.delete(key); | ||
| 395 | }); | ||
| 396 | }; | 19 | }; |
| 397 | 20 | ||
| 398 | export const refreshSourceTypeDeclarationInBackground = (input: { | 21 | export class SourceTypeDeclarationsRefresherService extends Context.Tag( |
| 399 | context: ResolvedLocalWorkspaceContext; | 22 | "#runtime/SourceTypeDeclarationsRefresherService", |
| 400 | source: Source; | 23 | )<SourceTypeDeclarationsRefresherService, SourceTypeDeclarationsRefresherShape>() {} |
| 401 | snapshot: CatalogSnapshotV1 | null; | ||
| 402 | }): void => { | ||
| 403 | const key = `${input.context.configDirectory}:${input.source.id}`; | ||
| 404 | const existingFiber = sourceRefreshFibers.get(key); | ||
| 405 | if (existingFiber) { | ||
| 406 | Effect.runFork(Fiber.interruptFork(existingFiber)); | ||
| 407 | } | ||
| 408 | |||
| 409 | const fiber = Effect.runFork( | ||
| 410 | Effect.sleep(BACKGROUND_DECLARATION_REFRESH_DELAY).pipe( | ||
| 411 | Effect.zipRight( | ||
| 412 | syncSourceTypeDeclarationNode(input).pipe( | ||
| 413 | Effect.catchAllCause((cause) => | ||
| 414 | Effect.sync(() => { | ||
| 415 | logBackgroundDeclarationError(`source ${input.source.id} declaration refresh`, cause); | ||
| 416 | }) | ||
| 417 | ), | ||
| 418 | ), | ||
| 419 | ), | ||
| 420 | ), | ||
| 421 | ); | ||
| 422 | |||
| 423 | sourceRefreshFibers.set(key, fiber); | ||
| 424 | fiber.addObserver(() => { | ||
| 425 | sourceRefreshFibers.delete(key); | ||
| 426 | }); | ||
| 427 | }; | ||
| @@ -31,28 +31,26 @@ import { · 30 unmodified lines | |||
| 31 | provideControlPlaneRuntime, | 31 | provideControlPlaneRuntime, |
| 32 | RuntimeExecutionResolverService, | 32 | RuntimeExecutionResolverService, |
| 33 | } from "./index"; | 33 | } from "./index"; |
| 34 | import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../sdk-file/src/index"; | ||
| 35 | import { createSourceFromPayload } from "./sources/source-definitions"; | 34 | import { createSourceFromPayload } from "./sources/source-definitions"; |
| 36 | import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions"; | 35 | import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions"; |
| 37 | import { persistSource } from "./sources/source-store"; | 36 | import { persistSource } from "./sources/source-store"; |
| 38 | import { withControlPlaneClient } from "./execution/test-http-client"; | 37 | import { withControlPlaneClient } from "./execution/test-http-client"; |
| 39 | import { runtimeEffectError } from "./effect-errors"; | 38 | import { runtimeEffectError } from "./effect-errors"; |
| 40 | import { | 39 | import { |
| 40 | buildLocalSourceArtifact, | ||
| 41 | createLocalControlPlaneRuntime as createControlPlaneRuntime, | ||
| 42 | deriveLocalInstallation, | ||
| 43 | readLocalSourceArtifact, | ||
| 41 | resolveLocalWorkspaceContext, | 44 | resolveLocalWorkspaceContext, |
| 45 | writeLocalControlPlaneState, | ||
| 46 | writeLocalSourceArtifact, | ||
| 42 | writeProjectLocalExecutorConfig, | 47 | writeProjectLocalExecutorConfig, |
| 43 | } from "./local/config"; | 48 | writeLocalWorkspaceState, |
| 44 | import { writeLocalControlPlaneState } from "./local/control-plane-store"; | 49 | } from "../../../sdk-file/src/index"; |
| 45 | import { deriveLocalInstallation } from "./local/installation"; | ||
| 46 | import { | 50 | import { |
| 47 | decodeSourceCredentialInteraction, | 51 | decodeSourceCredentialInteraction, |
| 48 | submitSourceCredentialInteraction, | 52 | submitSourceCredentialInteraction, |
| 49 | } from "./local/operations"; | 53 | } from "../local/operations"; |
| 50 | import { | ||
| 51 | buildLocalSourceArtifact, | ||
| 52 | readLocalSourceArtifact, | ||
| 53 | writeLocalSourceArtifact, | ||
| 54 | } from "./local/source-artifacts"; | ||
| 55 | import { writeLocalWorkspaceState } from "./local/workspace-state"; | ||
| 56 | 54 | ||
| 57 | const makeRuntime = Effect.gen(function* () { | 55 | const makeRuntime = Effect.gen(function* () { |
| 58 | const fs = yield* FileSystem.FileSystem; | 56 | const fs = yield* FileSystem.FileSystem; |
| @@ -38,7 +38,7 @@ import { · 37 unmodified lines | |||
| 38 | import { | 38 | import { |
| 39 | getRuntimeLocalWorkspaceOption, | 39 | getRuntimeLocalWorkspaceOption, |
| 40 | provideOptionalRuntimeLocalWorkspace, | 40 | provideOptionalRuntimeLocalWorkspace, |
| 41 | } from "../local/runtime-context"; | 41 | } from "../workspace/runtime-context"; |
| 42 | import { | 42 | import { |
| 43 | asOperationErrors, | 43 | asOperationErrors, |
| 44 | operationErrors, | 44 | operationErrors, |
| @@ -5,12 +5,11 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines | |||
| 5 | import * as Schema from "effect/Schema"; | 5 | import * as Schema from "effect/Schema"; |
| 6 | 6 | ||
| 7 | import type { LoadedSourceCatalogToolIndexEntry } from "../../catalog/source/runtime"; | 7 | import type { LoadedSourceCatalogToolIndexEntry } from "../../catalog/source/runtime"; |
| 8 | import type { SecretMaterialResolveContext } from "../../local/secret-material-providers"; | 8 | import type { SecretMaterialResolveContext } from "../../workspace/secret-material-providers"; |
| 9 | import type { WorkspaceStorageServices } from "../../local/storage"; | 9 | import type { WorkspaceStorageServices } from "../../workspace/storage"; |
| 10 | import { invocationDescriptorFromTool } from "../ir-execution"; | 10 | import { invocationDescriptorFromTool } from "../ir-execution"; |
| 11 | import { evaluateInvocationPolicy } from "../../policy/invocation-policy-engine"; | 11 | import { evaluateInvocationPolicy } from "../../policy/invocation-policy-engine"; |
| 12 | import { loadRuntimeLocalWorkspacePolicies } from "../../policy/policies-operations"; | 12 | import { loadRuntimeLocalWorkspacePolicies } from "../../../policies/operations"; |
| 13 | import { runtimeEffectError } from "../../effect-errors"; | ||
| 14 | 13 | ||
| 15 | const asToolPath = (value: string): ToolPath => value as ToolPath; | 14 | const asToolPath = (value: string): ToolPath => value as ToolPath; |
| 16 | 15 | ||
| @@ -62,6 +61,9 @@ export const toSecretResolutionContext = ( · 45 unmodified lines | |||
| 62 | }; | 61 | }; |
| 63 | }; | 62 | }; |
| 64 | 63 | ||
| 64 | const failAuthorization = (message: string): Effect.Effect<never, Error, never> => | ||
| 65 | Effect.fail(new Error(message)); | ||
| 66 | |||
| 65 | type WorkspaceToolElicitation = Parameters< | 67 | type WorkspaceToolElicitation = Parameters< |
| 66 | typeof makeToolInvokerFromTools | 68 | typeof makeToolInvokerFromTools |
| 67 | >[0]["onElicitation"]; | 69 | >[0]["onElicitation"]; |
| @@ -99,13 +101,13 @@ export const authorizePersistedToolInvocation = (input: { · 31 unmodified lines | |||
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | if (decision.kind === "deny") { | 103 | if (decision.kind === "deny") { |
| 102 | return yield* runtimeEffectError("execution/workspace/authorization", decision.reason); | 104 | return yield* failAuthorization(decision.reason); |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 105 | if (!input.onElicitation) { | 107 | if (!input.onElicitation) { |
| 106 | return yield* runtimeEffectError("execution/workspace/authorization", | 108 | return yield* failAuthorization( |
| 107 | `Approval required for ${descriptor.toolPath}, but no elicitation-capable host is available`, | 109 | `Approval required for ${descriptor.toolPath}, but no elicitation-capable host is available`, |
| 108 | ); | 110 | ); |
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | const interactionId = | 113 | const interactionId = |
| @@ -144,8 +146,8 @@ export const authorizePersistedToolInvocation = (input: { · 32 unmodified lines | |||
| 144 | ); | 146 | ); |
| 145 | 147 | ||
| 146 | if (response.action !== "accept") { | 148 | if (response.action !== "accept") { |
| 147 | return yield* runtimeEffectError("execution/workspace/authorization", | 149 | return yield* failAuthorization( |
| 148 | `Tool invocation not approved for ${descriptor.toolPath}`, | 150 | `Tool invocation not approved for ${descriptor.toolPath}`, |
| 149 | ); | 151 | ); |
| 150 | } | 152 | } |
| 151 | }); | 153 | }); |
| @@ -19,12 +19,18 @@ import { RuntimeSourceAuthServiceTag } from "../../sources/source-auth-service"; · 18 unmodified lines | |||
| 19 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; | 19 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; |
| 20 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; | 20 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; |
| 21 | import { RuntimeSourceCatalogSyncService } from "../../catalog/source/sync"; | 21 | import { RuntimeSourceCatalogSyncService } from "../../catalog/source/sync"; |
| 22 | import { getRuntimeLocalWorkspaceOption } from "../../local/runtime-context"; | 22 | import { getRuntimeLocalWorkspaceOption } from "../../workspace/runtime-context"; |
| 23 | import { | ||
| 24 | LocalInstanceConfigService, | ||
| 25 | SecretMaterialDeleterService, | ||
| 26 | SecretMaterialStorerService, | ||
| 27 | SecretMaterialUpdaterService, | ||
| 28 | } from "../../workspace/secret-material-providers"; | ||
| 23 | import { | 29 | import { |
| 24 | LocalToolRuntimeLoaderService, | 30 | LocalToolRuntimeLoaderService, |
| 25 | type LocalToolRuntimeLoaderShape, | 31 | type LocalToolRuntimeLoaderShape, |
| 26 | type LocalToolRuntime, | 32 | type LocalToolRuntime, |
| 27 | } from "../../local/tools"; | 33 | } from "../../local-tool-runtime"; |
| 28 | import { | 34 | import { |
| 29 | InstallationStore, | 35 | InstallationStore, |
| 30 | type InstallationStoreShape, | 36 | type InstallationStoreShape, |
| @@ -34,7 +40,7 @@ import { · 3 unmodified lines | |||
| 34 | type WorkspaceConfigStoreShape, | 40 | type WorkspaceConfigStoreShape, |
| 35 | WorkspaceStateStore, | 41 | WorkspaceStateStore, |
| 36 | type WorkspaceStateStoreShape, | 42 | type WorkspaceStateStoreShape, |
| 37 | } from "../../local/storage"; | 43 | } from "../../workspace/storage"; |
| 38 | import { ControlPlaneStore } from "../../store"; | 44 | import { ControlPlaneStore } from "../../store"; |
| 39 | import { RuntimeSourceStoreService } from "../../sources/source-store"; | 45 | import { RuntimeSourceStoreService } from "../../sources/source-store"; |
| 40 | import type { CreateWorkspaceInternalToolMap } from "./tool-invoker"; | 46 | import type { CreateWorkspaceInternalToolMap } from "./tool-invoker"; |
| @@ -68,6 +74,10 @@ export const createWorkspaceExecutionEnvironmentResolver = · 27 unmodified lines | |||
| 68 | >; | 74 | >; |
| 69 | localToolRuntimeLoader: LocalToolRuntimeLoaderShape; | 75 | localToolRuntimeLoader: LocalToolRuntimeLoaderShape; |
| 70 | installationStore: InstallationStoreShape; | 76 | installationStore: InstallationStoreShape; |
| 77 | instanceConfigResolver: Effect.Effect.Success<typeof LocalInstanceConfigService>; | ||
| 78 | storeSecretMaterial: Effect.Effect.Success<typeof SecretMaterialStorerService>; | ||
| 79 | deleteSecretMaterial: Effect.Effect.Success<typeof SecretMaterialDeleterService>; | ||
| 80 | updateSecretMaterial: Effect.Effect.Success<typeof SecretMaterialUpdaterService>; | ||
| 71 | workspaceConfigStore: WorkspaceConfigStoreShape; | 81 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 72 | workspaceStateStore: WorkspaceStateStoreShape; | 82 | workspaceStateStore: WorkspaceStateStoreShape; |
| 73 | sourceArtifactStore: SourceArtifactStoreShape; | 83 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -79,15 +89,11 @@ export const createWorkspaceExecutionEnvironmentResolver = · 5 unmodified lines | |||
| 79 | const loadedConfig = | 89 | const loadedConfig = |
| 80 | runtimeLocalWorkspace === null | 90 | runtimeLocalWorkspace === null |
| 81 | ? null | 91 | ? null |
| 82 | : yield* input.workspaceConfigStore.load( | 92 | : yield* input.workspaceConfigStore.load(); |
| 83 | runtimeLocalWorkspace.context, | ||
| 84 | ); | ||
| 85 | const localToolRuntime = | 93 | const localToolRuntime = |
| 86 | runtimeLocalWorkspace === null | 94 | runtimeLocalWorkspace === null |
| 87 | ? createEmptyLocalToolRuntime() | 95 | ? createEmptyLocalToolRuntime() |
| 88 | : yield* input.localToolRuntimeLoader.load( | 96 | : yield* input.localToolRuntimeLoader.load(); |
| 89 | runtimeLocalWorkspace.context, | ||
| 90 | ); | ||
| 91 | const { catalog, toolInvoker } = createWorkspaceToolInvoker({ | 97 | const { catalog, toolInvoker } = createWorkspaceToolInvoker({ |
| 92 | workspaceId, | 98 | workspaceId, |
| 93 | accountId, | 99 | accountId, |
| @@ -96,6 +102,10 @@ export const createWorkspaceExecutionEnvironmentResolver = · 2 unmodified lines | |||
| 96 | sourceCatalogSyncService: input.sourceCatalogSyncService, | 102 | sourceCatalogSyncService: input.sourceCatalogSyncService, |
| 97 | sourceCatalogStore: input.sourceCatalogStore, | 103 | sourceCatalogStore: input.sourceCatalogStore, |
| 98 | installationStore: input.installationStore, | 104 | installationStore: input.installationStore, |
| 105 | instanceConfigResolver: input.instanceConfigResolver, | ||
| 106 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 107 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 108 | updateSecretMaterial: input.updateSecretMaterial, | ||
| 99 | workspaceConfigStore: input.workspaceConfigStore, | 109 | workspaceConfigStore: input.workspaceConfigStore, |
| 100 | workspaceStateStore: input.workspaceStateStore, | 110 | workspaceStateStore: input.workspaceStateStore, |
| 101 | sourceArtifactStore: input.sourceArtifactStore, | 111 | sourceArtifactStore: input.sourceArtifactStore, |
| @@ -146,6 +156,10 @@ export const RuntimeExecutionResolverLive = ( · 44 unmodified lines | |||
| 146 | const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService; | 156 | const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService; |
| 147 | const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService; | 157 | const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService; |
| 148 | const installationStore = yield* InstallationStore; | 158 | const installationStore = yield* InstallationStore; |
| 159 | const instanceConfigResolver = yield* LocalInstanceConfigService; | ||
| 160 | const storeSecretMaterial = yield* SecretMaterialStorerService; | ||
| 161 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 162 | const updateSecretMaterial = yield* SecretMaterialUpdaterService; | ||
| 149 | const workspaceConfigStore = yield* WorkspaceConfigStore; | 163 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 150 | const workspaceStateStore = yield* WorkspaceStateStore; | 164 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 151 | const sourceArtifactStore = yield* SourceArtifactStore; | 165 | const sourceArtifactStore = yield* SourceArtifactStore; |
| @@ -159,6 +173,10 @@ export const RuntimeExecutionResolverLive = ( · 7 unmodified lines | |||
| 159 | sourceCatalogStore, | 173 | sourceCatalogStore, |
| 160 | localToolRuntimeLoader, | 174 | localToolRuntimeLoader, |
| 161 | installationStore, | 175 | installationStore, |
| 176 | instanceConfigResolver, | ||
| 177 | storeSecretMaterial, | ||
| 178 | deleteSecretMaterial, | ||
| 179 | updateSecretMaterial, | ||
| 162 | workspaceConfigStore, | 180 | workspaceConfigStore, |
| 163 | workspaceStateStore, | 181 | workspaceStateStore, |
| 164 | sourceArtifactStore, | 182 | sourceArtifactStore, |
| @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; · 2 unmodified lines | |||
| 3 | import { | 3 | import { |
| 4 | provideOptionalRuntimeLocalWorkspace, | 4 | provideOptionalRuntimeLocalWorkspace, |
| 5 | type RuntimeLocalWorkspaceState, | 5 | type RuntimeLocalWorkspaceState, |
| 6 | } from "../../local/runtime-context"; | 6 | } from "../../workspace/runtime-context"; |
| 7 | 7 | ||
| 8 | export const provideRuntimeLocalWorkspace = <A, E, R>( | 8 | export const provideRuntimeLocalWorkspace = <A, E, R>( |
| 9 | effect: Effect.Effect<A, E, R>, | 9 | effect: Effect.Effect<A, E, R>, |
| @@ -10,14 +10,14 @@ import { · 9 unmodified lines | |||
| 10 | type LoadedSourceCatalogToolIndexEntry, | 10 | type LoadedSourceCatalogToolIndexEntry, |
| 11 | catalogToolCatalogEntry, | 11 | catalogToolCatalogEntry, |
| 12 | } from "../../catalog/source/runtime"; | 12 | } from "../../catalog/source/runtime"; |
| 13 | import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context"; | 13 | import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context"; |
| 14 | import { | 14 | import { |
| 15 | makeWorkspaceStorageLayer, | 15 | makeWorkspaceStorageLayer, |
| 16 | type SourceArtifactStoreShape, | 16 | type SourceArtifactStoreShape, |
| 17 | type WorkspaceConfigStoreShape, | 17 | type WorkspaceConfigStoreShape, |
| 18 | type WorkspaceStateStoreShape, | 18 | type WorkspaceStateStoreShape, |
| 19 | type WorkspaceStorageServices, | 19 | type WorkspaceStorageServices, |
| 20 | } from "../../local/storage"; | 20 | } from "../../workspace/storage"; |
| 21 | import { provideRuntimeLocalWorkspace } from "./local"; | 21 | import { provideRuntimeLocalWorkspace } from "./local"; |
| 22 | 22 | ||
| 23 | const tokenize = (value: string): string[] => | 23 | const tokenize = (value: string): string[] => |
| @@ -13,15 +13,21 @@ import * as Effect from "effect/Effect"; · 12 unmodified lines | |||
| 13 | 13 | ||
| 14 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; | 14 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; |
| 15 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; | 15 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; |
| 16 | import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context"; | 16 | import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context"; |
| 17 | import { type LocalToolRuntime } from "../../local/tools"; | 17 | import { type LocalToolRuntime } from "../../local-tool-runtime"; |
| 18 | import { | 18 | import { |
| 19 | type InstallationStoreShape, | 19 | type InstallationStoreShape, |
| 20 | makeWorkspaceStorageLayer, | 20 | makeWorkspaceStorageLayer, |
| 21 | type SourceArtifactStoreShape, | 21 | type SourceArtifactStoreShape, |
| 22 | type WorkspaceConfigStoreShape, | 22 | type WorkspaceConfigStoreShape, |
| 23 | type WorkspaceStateStoreShape, | 23 | type WorkspaceStateStoreShape, |
| 24 | } from "../../local/storage"; | 24 | } from "../../workspace/storage"; |
| 25 | import type { | ||
| 26 | DeleteSecretMaterial, | ||
| 27 | ResolveInstanceConfig, | ||
| 28 | StoreSecretMaterial, | ||
| 29 | UpdateSecretMaterial, | ||
| 30 | } from "../../workspace/secret-material-providers"; | ||
| 25 | import type { ControlPlaneStoreShape } from "../../store"; | 31 | import type { ControlPlaneStoreShape } from "../../store"; |
| 26 | import { RuntimeSourceAuthService } from "../../sources/source-auth-service"; | 32 | import { RuntimeSourceAuthService } from "../../sources/source-auth-service"; |
| 27 | import { type RuntimeSourceStore } from "../../sources/source-store"; | 33 | import { type RuntimeSourceStore } from "../../sources/source-store"; |
| @@ -49,6 +55,10 @@ export type WorkspaceInternalToolContext = { · 21 unmodified lines | |||
| 49 | >; | 55 | >; |
| 50 | sourceAuthService: RuntimeSourceAuthService; | 56 | sourceAuthService: RuntimeSourceAuthService; |
| 51 | installationStore: InstallationStoreShape; | 57 | installationStore: InstallationStoreShape; |
| 58 | instanceConfigResolver: ResolveInstanceConfig; | ||
| 59 | storeSecretMaterial: StoreSecretMaterial; | ||
| 60 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 61 | updateSecretMaterial: UpdateSecretMaterial; | ||
| 52 | workspaceConfigStore: WorkspaceConfigStoreShape; | 62 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 53 | workspaceStateStore: WorkspaceStateStoreShape; | 63 | workspaceStateStore: WorkspaceStateStoreShape; |
| 54 | sourceArtifactStore: SourceArtifactStoreShape; | 64 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -71,6 +81,10 @@ export const createWorkspaceToolInvoker = (input: { · 16 unmodified lines | |||
| 71 | typeof RuntimeSourceCatalogStoreService | 81 | typeof RuntimeSourceCatalogStoreService |
| 72 | >; | 82 | >; |
| 73 | installationStore: InstallationStoreShape; | 83 | installationStore: InstallationStoreShape; |
| 84 | instanceConfigResolver: ResolveInstanceConfig; | ||
| 85 | storeSecretMaterial: StoreSecretMaterial; | ||
| 86 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 87 | updateSecretMaterial: UpdateSecretMaterial; | ||
| 74 | workspaceConfigStore: WorkspaceConfigStoreShape; | 88 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 75 | workspaceStateStore: WorkspaceStateStoreShape; | 89 | workspaceStateStore: WorkspaceStateStoreShape; |
| 76 | sourceArtifactStore: SourceArtifactStoreShape; | 90 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -115,6 +129,10 @@ export const createWorkspaceToolInvoker = (input: { · 38 unmodified lines | |||
| 115 | sourceCatalogSyncService: input.sourceCatalogSyncService, | 129 | sourceCatalogSyncService: input.sourceCatalogSyncService, |
| 116 | sourceAuthService: input.sourceAuthService, | 130 | sourceAuthService: input.sourceAuthService, |
| 117 | installationStore: input.installationStore, | 131 | installationStore: input.installationStore, |
| 132 | instanceConfigResolver: input.instanceConfigResolver, | ||
| 133 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 134 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 135 | updateSecretMaterial: input.updateSecretMaterial, | ||
| 118 | workspaceConfigStore: input.workspaceConfigStore, | 136 | workspaceConfigStore: input.workspaceConfigStore, |
| 119 | workspaceStateStore: input.workspaceStateStore, | 137 | workspaceStateStore: input.workspaceStateStore, |
| 120 | sourceArtifactStore: input.sourceArtifactStore, | 138 | sourceArtifactStore: input.sourceArtifactStore, |
| @@ -13,16 +13,22 @@ import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material"; · 12 unmodified lines | |||
| 13 | import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime"; | 13 | import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime"; |
| 14 | import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile"; | 14 | import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile"; |
| 15 | import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync"; | 15 | import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync"; |
| 16 | import { | ||
| 17 | SourceTypeDeclarationsRefresherService, | ||
| 18 | type SourceTypeDeclarationsRefresherShape, | ||
| 19 | } from "./catalog/source/type-declarations"; | ||
| 16 | import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live"; | 20 | import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live"; |
| 17 | import { RuntimeExecutionResolverLive } from "./execution/workspace/environment"; | 21 | import { RuntimeExecutionResolverLive } from "./execution/workspace/environment"; |
| 18 | import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker"; | 22 | import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker"; |
| 19 | import type { | 23 | import type { LoadedLocalExecutorConfig } from "./workspace-config"; |
| 20 | LoadedLocalExecutorConfig, | ||
| 21 | ResolvedLocalWorkspaceContext, | ||
| 22 | } from "./local/config"; | ||
| 23 | import type { LocalExecutorConfig } from "#schema"; | 24 | import type { LocalExecutorConfig } from "#schema"; |
| 24 | import type { LocalControlPlanePersistence } from "./local/control-plane-store"; | 25 | import type { InstanceConfig } from "../local/contracts"; |
| 26 | import type { | ||
| 27 | ExecutorWorkspaceContext, | ||
| 28 | ExecutorWorkspaceDescriptor, | ||
| 29 | } from "../workspace"; | ||
| 25 | import { | 30 | import { |
| 31 | LocalInstanceConfigService, | ||
| 26 | type DeleteSecretMaterial, | 32 | type DeleteSecretMaterial, |
| 27 | type ResolveSecretMaterial, | 33 | type ResolveSecretMaterial, |
| 28 | SecretMaterialDeleterService, | 34 | SecretMaterialDeleterService, |
| @@ -31,17 +37,17 @@ import { · 2 unmodified lines | |||
| 31 | SecretMaterialUpdaterService, | 37 | SecretMaterialUpdaterService, |
| 32 | type StoreSecretMaterial, | 38 | type StoreSecretMaterial, |
| 33 | type UpdateSecretMaterial, | 39 | type UpdateSecretMaterial, |
| 34 | } from "./local/secret-material-providers"; | 40 | } from "./workspace/secret-material-providers"; |
| 35 | import type { LocalSourceArtifact } from "./local/source-artifacts"; | 41 | import type { LocalSourceArtifact } from "./source-artifacts"; |
| 36 | import { | 42 | import { |
| 37 | type RuntimeLocalWorkspaceState, | 43 | type RuntimeLocalWorkspaceState, |
| 38 | RuntimeLocalWorkspaceLive, | 44 | RuntimeLocalWorkspaceLive, |
| 39 | } from "./local/runtime-context"; | 45 | } from "./workspace/runtime-context"; |
| 40 | import { | 46 | import { |
| 41 | type LocalToolRuntime, | 47 | type LocalToolRuntime, |
| 42 | type LocalToolRuntimeLoaderShape, | 48 | type LocalToolRuntimeLoaderShape, |
| 43 | LocalToolRuntimeLoaderService, | 49 | LocalToolRuntimeLoaderService, |
| 44 | } from "./local/tools"; | 50 | } from "./local-tool-runtime"; |
| 45 | import { | 51 | import { |
| 46 | InstallationStore, | 52 | InstallationStore, |
| 47 | makeLocalStorageLayer, | 53 | makeLocalStorageLayer, |
| @@ -49,9 +55,9 @@ import { · 1 unmodified line | |||
| 49 | type SourceArtifactStoreShape, | 55 | type SourceArtifactStoreShape, |
| 50 | type WorkspaceConfigStoreShape, | 56 | type WorkspaceConfigStoreShape, |
| 51 | type WorkspaceStateStoreShape, | 57 | type WorkspaceStateStoreShape, |
| 52 | } from "./local/storage"; | 58 | } from "./workspace/storage"; |
| 53 | import type { LocalWorkspaceState } from "./local/workspace-state"; | 59 | import type { LocalWorkspaceState } from "./workspace-state"; |
| 54 | import { synchronizeLocalWorkspaceState } from "./local/workspace-sync"; | 60 | import { synchronizeLocalWorkspaceState } from "./workspace/workspace-sync"; |
| 55 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store"; | 61 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store"; |
| 56 | import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service"; | 62 | import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service"; |
| 57 | import { RuntimeSourceStoreLive } from "./sources/source-store"; | 63 | import { RuntimeSourceStoreLive } from "./sources/source-store"; |
| @@ -59,19 +65,10 @@ import { RuntimeSourceStoreLive } from "./sources/source-store"; · 1 unmodified line | |||
| 59 | export * from "./execution/state"; | 65 | export * from "./execution/state"; |
| 60 | export * from "./sources/executor-tools"; | 66 | export * from "./sources/executor-tools"; |
| 61 | export * from "./execution/live"; | 67 | export * from "./execution/live"; |
| 62 | export * from "./local/config"; | ||
| 63 | export * from "./local/control-plane-store"; | ||
| 64 | export * from "./local/errors"; | ||
| 65 | export * from "./local/installation"; | ||
| 66 | export * from "./local/runtime-context"; | ||
| 67 | export * from "./local/storage"; | ||
| 68 | export * from "./local/source-artifacts"; | ||
| 69 | export * from "./local/tools"; | ||
| 70 | export * from "./catalog/schema-type-signature"; | 68 | export * from "./catalog/schema-type-signature"; |
| 71 | export * from "./catalog/source/runtime"; | 69 | export * from "./catalog/source/runtime"; |
| 72 | export * from "./catalog/source/sync"; | 70 | export * from "./catalog/source/sync"; |
| 73 | export * from "./sources/source-auth-service"; | 71 | export * from "./sources/source-auth-service"; |
| 74 | export * from "./local/secret-material-providers"; | ||
| 75 | export * from "./sources/source-credential-interactions"; | 72 | export * from "./sources/source-credential-interactions"; |
| 76 | export * from "./sources/source-adapters/mcp"; | 73 | export * from "./sources/source-adapters/mcp"; |
| 77 | export * from "./sources/source-store"; | 74 | export * from "./sources/source-store"; |
| @@ -84,6 +81,20 @@ export type { · 6 unmodified lines | |||
| 84 | CreateWorkspaceInternalToolMap, | 81 | CreateWorkspaceInternalToolMap, |
| 85 | WorkspaceInternalToolContext, | 82 | WorkspaceInternalToolContext, |
| 86 | } from "./execution/workspace/tool-invoker"; | 83 | } from "./execution/workspace/tool-invoker"; |
| 84 | export { | ||
| 85 | LocalInstanceConfigService, | ||
| 86 | SecretMaterialDeleterService, | ||
| 87 | SecretMaterialResolverService, | ||
| 88 | SecretMaterialStorerService, | ||
| 89 | SecretMaterialUpdaterService, | ||
| 90 | } from "./workspace/secret-material-providers"; | ||
| 91 | export type { | ||
| 92 | DeleteSecretMaterial, | ||
| 93 | ResolveInstanceConfig, | ||
| 94 | ResolveSecretMaterial, | ||
| 95 | StoreSecretMaterial, | ||
| 96 | UpdateSecretMaterial, | ||
| 97 | } from "./workspace/secret-material-providers"; | ||
| 87 | export { | 98 | export { |
| 88 | builtInSourceAdapters, | 99 | builtInSourceAdapters, |
| 89 | connectableSourceAdapters, | 100 | connectableSourceAdapters, |
| @@ -154,6 +165,9 @@ export type BoundLocalToolRuntimeLoader = { · 64 unmodified lines | |||
| 154 | load: () => ReturnType<LocalToolRuntimeLoaderShape["load"]>; | 165 | load: () => ReturnType<LocalToolRuntimeLoaderShape["load"]>; |
| 155 | }; | 166 | }; |
| 156 | 167 | ||
| 168 | export type BoundSourceTypeDeclarationsRefresher = | ||
| 169 | SourceTypeDeclarationsRefresherShape; | ||
| 170 | |||
| 157 | export type RuntimeSecretMaterialServices = { | 171 | export type RuntimeSecretMaterialServices = { |
| 158 | resolve: ResolveSecretMaterial; | 172 | resolve: ResolveSecretMaterial; |
| 159 | store: StoreSecretMaterial; | 173 | store: StoreSecretMaterial; |
| @@ -161,15 +175,26 @@ export type RuntimeSecretMaterialServices = { · 1 unmodified line | |||
| 161 | update: UpdateSecretMaterial; | 175 | update: UpdateSecretMaterial; |
| 162 | }; | 176 | }; |
| 163 | 177 | ||
| 178 | export type RuntimeInstanceConfigService = { | ||
| 179 | resolve: () => Effect.Effect<InstanceConfig, Error, never>; | ||
| 180 | }; | ||
| 181 | |||
| 182 | export type RuntimePersistence = { | ||
| 183 | rows: ControlPlaneStoreShape; | ||
| 184 | close: () => Promise<void>; | ||
| 185 | }; | ||
| 186 | |||
| 164 | export type RuntimeControlPlaneServices = { | 187 | export type RuntimeControlPlaneServices = { |
| 165 | workspaceContext: ResolvedLocalWorkspaceContext; | 188 | workspace: ExecutorWorkspaceDescriptor; |
| 166 | installationStore: BoundInstallationStore; | 189 | installationStore: BoundInstallationStore; |
| 167 | workspaceConfigStore: BoundWorkspaceConfigStore; | 190 | workspaceConfigStore: BoundWorkspaceConfigStore; |
| 168 | workspaceStateStore: BoundWorkspaceStateStore; | 191 | workspaceStateStore: BoundWorkspaceStateStore; |
| 169 | sourceArtifactStore: BoundSourceArtifactStore; | 192 | sourceArtifactStore: BoundSourceArtifactStore; |
| 170 | localToolRuntimeLoader?: BoundLocalToolRuntimeLoader; | 193 | localToolRuntimeLoader?: BoundLocalToolRuntimeLoader; |
| 194 | sourceTypeDeclarationsRefresher?: BoundSourceTypeDeclarationsRefresher; | ||
| 171 | secretMaterial: RuntimeSecretMaterialServices; | 195 | secretMaterial: RuntimeSecretMaterialServices; |
| 172 | persistence: LocalControlPlanePersistence; | 196 | instanceConfig: RuntimeInstanceConfigService; |
| 197 | persistence: RuntimePersistence; | ||
| 173 | }; | 198 | }; |
| 174 | 199 | ||
| 175 | const emptyToolRuntime = (): LocalToolRuntime => { | 200 | const emptyToolRuntime = (): LocalToolRuntime => { |
| @@ -182,17 +207,22 @@ const emptyToolRuntime = (): LocalToolRuntime => { · 6 unmodified lines | |||
| 182 | }; | 207 | }; |
| 183 | }; | 208 | }; |
| 184 | 209 | ||
| 210 | const noopSourceTypeDeclarationsRefresher: BoundSourceTypeDeclarationsRefresher = { | ||
| 211 | refreshWorkspaceInBackground: () => Effect.void, | ||
| 212 | refreshSourceInBackground: () => Effect.void, | ||
| 213 | }; | ||
| 214 | |||
| 185 | const toInstallationStoreShape = ( | 215 | const toInstallationStoreShape = ( |
| 186 | input: BoundInstallationStore, | 216 | input: BoundInstallationStore, |
| 187 | ): InstallationStoreShape => ({ | 217 | ): InstallationStoreShape => ({ |
| 188 | load: () => input.load(), | 218 | load: input.load, |
| 189 | getOrProvision: () => input.getOrProvision(), | 219 | getOrProvision: input.getOrProvision, |
| 190 | }); | 220 | }); |
| 191 | 221 | ||
| 192 | const toWorkspaceConfigStoreShape = ( | 222 | const toWorkspaceConfigStoreShape = ( |
| 193 | input: BoundWorkspaceConfigStore, | 223 | input: BoundWorkspaceConfigStore, |
| 194 | ): WorkspaceConfigStoreShape => ({ | 224 | ): WorkspaceConfigStoreShape => ({ |
| 195 | load: () => input.load(), | 225 | load: input.load, |
| 196 | writeProject: ({ config }) => input.writeProject(config), | 226 | writeProject: ({ config }) => input.writeProject(config), |
| 197 | resolveRelativePath: input.resolveRelativePath, | 227 | resolveRelativePath: input.resolveRelativePath, |
| 198 | }); | 228 | }); |
| @@ -200,7 +230,7 @@ const toWorkspaceConfigStoreShape = ( · 1 unmodified line | |||
| 200 | const toWorkspaceStateStoreShape = ( | 230 | const toWorkspaceStateStoreShape = ( |
| 201 | input: BoundWorkspaceStateStore, | 231 | input: BoundWorkspaceStateStore, |
| 202 | ): WorkspaceStateStoreShape => ({ | 232 | ): WorkspaceStateStoreShape => ({ |
| 203 | load: () => input.load(), | 233 | load: input.load, |
| 204 | write: ({ state }) => input.write(state), | 234 | write: ({ state }) => input.write(state), |
| 205 | }); | 235 | }); |
| 206 | 236 | ||
| @@ -221,6 +251,9 @@ const makeSecretMaterialLayer = (input: RuntimeSecretMaterialServices) => · 14 unmodified lines | |||
| 221 | Layer.succeed(SecretMaterialUpdaterService, input.update), | 251 | Layer.succeed(SecretMaterialUpdaterService, input.update), |
| 222 | ); | 252 | ); |
| 223 | 253 | ||
| 254 | const makeInstanceConfigLayer = (input: RuntimeInstanceConfigService) => | ||
| 255 | Layer.succeed(LocalInstanceConfigService, input.resolve); | ||
| 256 | |||
| 224 | export const createRuntimeControlPlaneLayer = ( | 257 | export const createRuntimeControlPlaneLayer = ( |
| 225 | input: RuntimeControlPlaneOptions & RuntimeControlPlaneServices & { | 258 | input: RuntimeControlPlaneOptions & RuntimeControlPlaneServices & { |
| 226 | store: ControlPlaneStoreShape; | 259 | store: ControlPlaneStoreShape; |
| @@ -241,19 +274,30 @@ export const createRuntimeControlPlaneLayer = ( · 14 unmodified lines | |||
| 241 | input.localToolRuntimeLoader?.load() ?? Effect.succeed(emptyToolRuntime()), | 274 | input.localToolRuntimeLoader?.load() ?? Effect.succeed(emptyToolRuntime()), |
| 242 | }), | 275 | }), |
| 243 | ); | 276 | ); |
| 277 | const sourceTypeDeclarationsRefresherLayer = Layer.succeed( | ||
| 278 | SourceTypeDeclarationsRefresherService, | ||
| 279 | SourceTypeDeclarationsRefresherService.of( | ||
| 280 | input.sourceTypeDeclarationsRefresher | ||
| 281 | ?? noopSourceTypeDeclarationsRefresher, | ||
| 282 | ), | ||
| 283 | ); | ||
| 244 | 284 | ||
| 245 | const baseLayer = Layer.mergeAll( | 285 | const baseLayer = Layer.mergeAll( |
| 246 | Layer.succeed(ControlPlaneStore, input.store), | 286 | Layer.succeed(ControlPlaneStore, input.store), |
| 247 | RuntimeLocalWorkspaceLive(input.localWorkspaceState), | 287 | RuntimeLocalWorkspaceLive(input.localWorkspaceState), |
| 248 | storageLayer, | 288 | storageLayer, |
| 249 | Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager), | 289 | Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager), |
| 290 | sourceTypeDeclarationsRefresherLayer, | ||
| 250 | ); | 291 | ); |
| 251 | 292 | ||
| 252 | const secretMaterialLayer = makeSecretMaterialLayer(input.secretMaterial).pipe( | 293 | const secretMaterialLayer = makeSecretMaterialLayer(input.secretMaterial).pipe( |
| 253 | Layer.provide(baseLayer), | 294 | Layer.provide(baseLayer), |
| 254 | ); | 295 | ); |
| 296 | const instanceConfigLayer = makeInstanceConfigLayer(input.instanceConfig); | ||
| 255 | 297 | ||
| 256 | const sourceStoreLayer = RuntimeSourceStoreLive.pipe(Layer.provide(baseLayer)); | 298 | const sourceStoreLayer = RuntimeSourceStoreLive.pipe( |
| 299 | Layer.provide(Layer.mergeAll(baseLayer, secretMaterialLayer)), | ||
| 300 | ); | ||
| 257 | 301 | ||
| 258 | const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe( | 302 | const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe( |
| 259 | Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)), | 303 | Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)), |
| @@ -275,6 +319,7 @@ export const createRuntimeControlPlaneLayer = ( · 15 unmodified lines | |||
| 275 | Layer.provide( | 319 | Layer.provide( |
| 276 | Layer.mergeAll( | 320 | Layer.mergeAll( |
| 277 | baseLayer, | 321 | baseLayer, |
| 322 | instanceConfigLayer, | ||
| 278 | secretMaterialLayer, | 323 | secretMaterialLayer, |
| 279 | sourceStoreLayer, | 324 | sourceStoreLayer, |
| 280 | sourceCatalogSyncLayer, | 325 | sourceCatalogSyncLayer, |
| @@ -289,6 +334,7 @@ export const createRuntimeControlPlaneLayer = ( · 8 unmodified lines | |||
| 289 | Layer.provide( | 334 | Layer.provide( |
| 290 | Layer.mergeAll( | 335 | Layer.mergeAll( |
| 291 | baseLayer, | 336 | baseLayer, |
| 337 | instanceConfigLayer, | ||
| 292 | secretMaterialLayer, | 338 | secretMaterialLayer, |
| 293 | sourceStoreLayer, | 339 | sourceStoreLayer, |
| 294 | sourceAuthMaterialLayer, | 340 | sourceAuthMaterialLayer, |
| @@ -302,6 +348,7 @@ export const createRuntimeControlPlaneLayer = ( · 7 unmodified lines | |||
| 302 | 348 | ||
| 303 | return Layer.mergeAll( | 349 | return Layer.mergeAll( |
| 304 | baseLayer, | 350 | baseLayer, |
| 351 | instanceConfigLayer, | ||
| 305 | secretMaterialLayer, | 352 | secretMaterialLayer, |
| 306 | sourceStoreLayer, | 353 | sourceStoreLayer, |
| 307 | sourceAuthMaterialLayer, | 354 | sourceAuthMaterialLayer, |
| @@ -314,7 +361,7 @@ export const createRuntimeControlPlaneLayer = ( · 6 unmodified lines | |||
| 314 | }; | 361 | }; |
| 315 | 362 | ||
| 316 | export type ControlPlaneRuntime = { | 363 | export type ControlPlaneRuntime = { |
| 317 | persistence: LocalControlPlanePersistence; | 364 | persistence: RuntimePersistence; |
| 318 | localInstallation: LocalInstallation; | 365 | localInstallation: LocalInstallation; |
| 319 | managedRuntime: ManagedRuntime.ManagedRuntime<any, never>; | 366 | managedRuntime: ManagedRuntime.ManagedRuntime<any, never>; |
| 320 | runtimeLayer: RuntimeControlPlaneLayer; | 367 | runtimeLayer: RuntimeControlPlaneLayer; |
| @@ -339,8 +386,12 @@ export const createControlPlaneRuntimeFromServices = (input: { · 18 unmodified lines | |||
| 339 | .load() | 386 | .load() |
| 340 | .pipe(Effect.mapError(toRuntimeBootstrapError)); | 387 | .pipe(Effect.mapError(toRuntimeBootstrapError)); |
| 341 | 388 | ||
| 389 | const runtimeWorkspace: ExecutorWorkspaceContext = { | ||
| 390 | ...input.services.workspace, | ||
| 391 | workspaceId: localInstallation.workspaceId, | ||
| 392 | accountId: localInstallation.accountId, | ||
| 393 | }; | ||
| 342 | const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({ | 394 | const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({ |
| 343 | context: input.services.workspaceContext, | ||
| 344 | loadedConfig: loadedLocalConfig, | 395 | loadedConfig: loadedLocalConfig, |
| 345 | }) | 396 | }) |
| 346 | .pipe( | 397 | .pipe( |
| @@ -362,9 +413,8 @@ export const createControlPlaneRuntimeFromServices = (input: { · 15 unmodified lines | |||
| 362 | ), | 413 | ), |
| 363 | ) | 414 | ) |
| 364 | .pipe(Effect.mapError(toRuntimeBootstrapError)); | 415 | .pipe(Effect.mapError(toRuntimeBootstrapError)); |
| 365 | |||
| 366 | const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = { | 416 | const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = { |
| 367 | context: input.services.workspaceContext, | 417 | workspace: runtimeWorkspace, |
| 368 | installation: { | 418 | installation: { |
| 369 | workspaceId: localInstallation.workspaceId, | 419 | workspaceId: localInstallation.workspaceId, |
| 370 | accountId: localInstallation.accountId, | 420 | accountId: localInstallation.accountId, |
| @@ -0,0 +1,37 @@ | |||
| 1 | import type { | ||
| 2 | ToolCatalog, | ||
| 3 | ToolInvoker, | ||
| 4 | ToolMap, | ||
| 5 | } from "@executor/codemode-core"; | ||
| 6 | import * as Context from "effect/Context"; | ||
| 7 | import * as Effect from "effect/Effect"; | ||
| 8 | import * as Layer from "effect/Layer"; | ||
| 9 | |||
| 10 | export type LocalToolRuntime = { | ||
| 11 | tools: ToolMap; | ||
| 12 | catalog: ToolCatalog; | ||
| 13 | toolInvoker: ToolInvoker; | ||
| 14 | toolPaths: Set<string>; | ||
| 15 | }; | ||
| 16 | |||
| 17 | export type LocalToolRuntimeLoaderShape = { | ||
| 18 | load: () => Effect.Effect<LocalToolRuntime, Error, never>; | ||
| 19 | }; | ||
| 20 | |||
| 21 | export class LocalToolRuntimeLoaderService extends Context.Tag( | ||
| 22 | "#runtime/LocalToolRuntimeLoaderService", | ||
| 23 | )<LocalToolRuntimeLoaderService, LocalToolRuntimeLoaderShape>() {} | ||
| 24 | |||
| 25 | export const LocalToolRuntimeLoaderLive = Layer.effect( | ||
| 26 | LocalToolRuntimeLoaderService, | ||
| 27 | Effect.succeed( | ||
| 28 | LocalToolRuntimeLoaderService.of({ | ||
| 29 | load: () => | ||
| 30 | Effect.die( | ||
| 31 | new Error( | ||
| 32 | "LocalToolRuntimeLoaderLive is unsupported; provide a bound local tool runtime loader from the host adapter.", | ||
| 33 | ), | ||
| 34 | ), | ||
| 35 | }), | ||
| 36 | ), | ||
| 37 | ); | ||
| @@ -1 +0,0 @@ | |||
| 1 | export * from "../../local/operations"; | ||
| @@ -1,377 +0,0 @@ | |||
| 1 | import { join } from "node:path"; | ||
| 2 | import { FileSystem } from "@effect/platform"; | ||
| 3 | |||
| 4 | import { | ||
| 5 | SourceIdSchema, | ||
| 6 | SourceCatalogIdSchema, | ||
| 7 | StoredSourceCatalogRevisionRecordSchema, | ||
| 8 | TimestampMsSchema, | ||
| 9 | type Source, | ||
| 10 | type SourceCatalogId, | ||
| 11 | } from "#schema"; | ||
| 12 | import * as Effect from "effect/Effect"; | ||
| 13 | import * as Option from "effect/Option"; | ||
| 14 | import * as Schema from "effect/Schema"; | ||
| 15 | |||
| 16 | import { decodeCatalogSnapshotV1 } from "@executor/ir/catalog"; | ||
| 17 | import { | ||
| 18 | CatalogSnapshotV1Schema, | ||
| 19 | type CatalogSnapshotV1, | ||
| 20 | type NativeBlob, | ||
| 21 | type SourceDocument, | ||
| 22 | } from "@executor/ir/model"; | ||
| 23 | import { | ||
| 24 | contentHash, | ||
| 25 | snapshotFromSourceCatalogSyncResult, | ||
| 26 | type SourceCatalogSyncResult, | ||
| 27 | } from "@executor/source-core"; | ||
| 28 | import type { ResolvedLocalWorkspaceContext } from "./config"; | ||
| 29 | import { | ||
| 30 | LocalFileSystemError, | ||
| 31 | unknownLocalErrorDetails, | ||
| 32 | } from "./errors"; | ||
| 33 | import { | ||
| 34 | createSourceCatalogRevisionRecord, | ||
| 35 | stableSourceCatalogId, | ||
| 36 | } from "../sources/source-definitions"; | ||
| 37 | const LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION = 3 as const; | ||
| 38 | const LOCAL_SOURCE_ARTIFACT_VERSION = 4 as const; | ||
| 39 | |||
| 40 | const LegacyLocalSourceArtifactSchema = Schema.Struct({ | ||
| 41 | version: Schema.Literal(LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 42 | sourceId: SourceIdSchema, | ||
| 43 | catalogId: SourceCatalogIdSchema, | ||
| 44 | generatedAt: TimestampMsSchema, | ||
| 45 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 46 | snapshot: CatalogSnapshotV1Schema, | ||
| 47 | }); | ||
| 48 | |||
| 49 | export const LocalSourceArtifactSchema = Schema.Struct({ | ||
| 50 | version: Schema.Literal(LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 51 | sourceId: SourceIdSchema, | ||
| 52 | catalogId: SourceCatalogIdSchema, | ||
| 53 | generatedAt: TimestampMsSchema, | ||
| 54 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 55 | snapshot: CatalogSnapshotV1Schema, | ||
| 56 | }); | ||
| 57 | |||
| 58 | export type LocalSourceArtifact = typeof LocalSourceArtifactSchema.Type; | ||
| 59 | type LegacyLocalSourceArtifact = typeof LegacyLocalSourceArtifactSchema.Type; | ||
| 60 | |||
| 61 | const ReadableLegacyLocalSourceArtifactSchema = Schema.Struct({ | ||
| 62 | version: Schema.Literal(LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 63 | sourceId: SourceIdSchema, | ||
| 64 | catalogId: SourceCatalogIdSchema, | ||
| 65 | generatedAt: TimestampMsSchema, | ||
| 66 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 67 | snapshot: Schema.Unknown, | ||
| 68 | }); | ||
| 69 | |||
| 70 | const ReadableLocalSourceArtifactSchema = Schema.Struct({ | ||
| 71 | version: Schema.Literal(LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 72 | sourceId: SourceIdSchema, | ||
| 73 | catalogId: SourceCatalogIdSchema, | ||
| 74 | generatedAt: TimestampMsSchema, | ||
| 75 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 76 | snapshot: Schema.Unknown, | ||
| 77 | }); | ||
| 78 | |||
| 79 | type ReadableLegacyLocalSourceArtifact = | ||
| 80 | typeof ReadableLegacyLocalSourceArtifactSchema.Type; | ||
| 81 | type ReadableLocalSourceArtifact = | ||
| 82 | typeof ReadableLocalSourceArtifactSchema.Type; | ||
| 83 | |||
| 84 | const decodeReadableLocalSourceArtifactOption = Schema.decodeUnknownOption( | ||
| 85 | Schema.parseJson( | ||
| 86 | Schema.Union( | ||
| 87 | ReadableLocalSourceArtifactSchema, | ||
| 88 | ReadableLegacyLocalSourceArtifactSchema, | ||
| 89 | ), | ||
| 90 | ), | ||
| 91 | ); | ||
| 92 | |||
| 93 | const normalizeLocalSourceArtifact = ( | ||
| 94 | artifact: ReadableLocalSourceArtifact | ReadableLegacyLocalSourceArtifact, | ||
| 95 | ): Omit<LocalSourceArtifact, "snapshot"> & { snapshot: unknown } => | ||
| 96 | artifact.version === LOCAL_SOURCE_ARTIFACT_VERSION | ||
| 97 | ? artifact | ||
| 98 | : { | ||
| 99 | ...artifact, | ||
| 100 | version: LOCAL_SOURCE_ARTIFACT_VERSION, | ||
| 101 | }; | ||
| 102 | |||
| 103 | const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | ||
| 104 | new LocalFileSystemError({ | ||
| 105 | message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`, | ||
| 106 | action, | ||
| 107 | path, | ||
| 108 | details: unknownLocalErrorDetails(cause), | ||
| 109 | }); | ||
| 110 | |||
| 111 | const mutableRecord = <K extends string, V>(value: Readonly<Record<K, V>>): Record<K, V> => | ||
| 112 | value as Record<K, V>; | ||
| 113 | |||
| 114 | const localSourceArtifactPath = (input: { | ||
| 115 | context: ResolvedLocalWorkspaceContext; | ||
| 116 | sourceId: string; | ||
| 117 | }): string => | ||
| 118 | join( | ||
| 119 | input.context.artifactsDirectory, | ||
| 120 | "sources", | ||
| 121 | `${input.sourceId}.json`, | ||
| 122 | ); | ||
| 123 | |||
| 124 | const localSourceDocumentDirectory = (input: { | ||
| 125 | context: ResolvedLocalWorkspaceContext; | ||
| 126 | sourceId: string; | ||
| 127 | }): string => | ||
| 128 | join(input.context.artifactsDirectory, "sources", input.sourceId, "documents"); | ||
| 129 | |||
| 130 | const localSourceDocumentPath = (input: { | ||
| 131 | context: ResolvedLocalWorkspaceContext; | ||
| 132 | sourceId: string; | ||
| 133 | documentId: string; | ||
| 134 | }): string => | ||
| 135 | join(localSourceDocumentDirectory(input), `${input.documentId}.txt`); | ||
| 136 | |||
| 137 | const splitArtifactSourceDocuments = (artifact: LocalSourceArtifact): { | ||
| 138 | artifact: LocalSourceArtifact; | ||
| 139 | rawDocuments: ReadonlyArray<{ | ||
| 140 | documentId: string; | ||
| 141 | blob: NativeBlob; | ||
| 142 | content: string; | ||
| 143 | }>; | ||
| 144 | } => { | ||
| 145 | const nextArtifact = structuredClone(artifact); | ||
| 146 | const rawDocuments: Array<{ | ||
| 147 | documentId: string; | ||
| 148 | blob: NativeBlob; | ||
| 149 | content: string; | ||
| 150 | }> = []; | ||
| 151 | |||
| 152 | for (const [documentId, document] of Object.entries(nextArtifact.snapshot.catalog.documents)) { | ||
| 153 | const mutableDocument = document as SourceDocument & { native?: NativeBlob[] }; | ||
| 154 | const sourceDocumentBlob = document.native?.find((blob) => | ||
| 155 | blob.kind === "source_document" && typeof blob.value === "string" | ||
| 156 | ); | ||
| 157 | if (!sourceDocumentBlob || typeof sourceDocumentBlob.value !== "string") { | ||
| 158 | continue; | ||
| 159 | } | ||
| 160 | |||
| 161 | rawDocuments.push({ | ||
| 162 | documentId, | ||
| 163 | blob: sourceDocumentBlob, | ||
| 164 | content: sourceDocumentBlob.value, | ||
| 165 | }); | ||
| 166 | |||
| 167 | const remainingBlobs = (document.native ?? []).filter((blob) => blob !== sourceDocumentBlob); | ||
| 168 | if (remainingBlobs.length > 0) { | ||
| 169 | mutableDocument.native = remainingBlobs; | ||
| 170 | } else { | ||
| 171 | delete mutableDocument.native; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | return { | ||
| 176 | artifact: nextArtifact, | ||
| 177 | rawDocuments, | ||
| 178 | }; | ||
| 179 | }; | ||
| 180 | |||
| 181 | const hydrateArtifactSourceDocuments = (input: { | ||
| 182 | artifact: LocalSourceArtifact; | ||
| 183 | rawDocuments: Readonly<Record<string, NativeBlob>>; | ||
| 184 | }): LocalSourceArtifact => { | ||
| 185 | const nextArtifact = structuredClone(input.artifact); | ||
| 186 | const mutableDocuments = mutableRecord(nextArtifact.snapshot.catalog.documents); | ||
| 187 | |||
| 188 | for (const [documentId, rawDocument] of Object.entries(input.rawDocuments)) { | ||
| 189 | const document = mutableDocuments[documentId as keyof typeof mutableDocuments] as (SourceDocument & { native?: NativeBlob[] }) | undefined; | ||
| 190 | if (!document) { | ||
| 191 | continue; | ||
| 192 | } | ||
| 193 | |||
| 194 | const remainingBlobs = (document.native ?? []).filter((blob) => blob.kind !== "source_document"); | ||
| 195 | document.native = [rawDocument, ...remainingBlobs]; | ||
| 196 | } | ||
| 197 | |||
| 198 | return nextArtifact; | ||
| 199 | }; | ||
| 200 | |||
| 201 | const snapshotHash = (snapshot: CatalogSnapshotV1): string => | ||
| 202 | contentHash(JSON.stringify(snapshot)); | ||
| 203 | |||
| 204 | const importMetadataHash = (snapshot: { import: SourceCatalogSyncResult["importMetadata"] }): string => | ||
| 205 | contentHash(JSON.stringify(snapshot.import)); | ||
| 206 | |||
| 207 | const decodeCatalogSnapshotV1Option = ( | ||
| 208 | snapshot: unknown, | ||
| 209 | ): CatalogSnapshotV1 | null => { | ||
| 210 | try { | ||
| 211 | return decodeCatalogSnapshotV1(snapshot); | ||
| 212 | } catch { | ||
| 213 | return null; | ||
| 214 | } | ||
| 215 | }; | ||
| 216 | |||
| 217 | export const buildLocalSourceArtifact = (input: { | ||
| 218 | source: Source; | ||
| 219 | syncResult: SourceCatalogSyncResult; | ||
| 220 | }): LocalSourceArtifact => { | ||
| 221 | const catalogId: SourceCatalogId = stableSourceCatalogId(input.source); | ||
| 222 | const generatedAt = Date.now(); | ||
| 223 | const snapshot = snapshotFromSourceCatalogSyncResult(input.syncResult); | ||
| 224 | const importHash = importMetadataHash(snapshot); | ||
| 225 | const hash = snapshotHash(snapshot); | ||
| 226 | const revision = createSourceCatalogRevisionRecord({ | ||
| 227 | source: input.source, | ||
| 228 | catalogId, | ||
| 229 | revisionNumber: 1, | ||
| 230 | importMetadataJson: JSON.stringify(snapshot.import), | ||
| 231 | importMetadataHash: importHash, | ||
| 232 | snapshotHash: hash, | ||
| 233 | }); | ||
| 234 | |||
| 235 | return { | ||
| 236 | version: LOCAL_SOURCE_ARTIFACT_VERSION, | ||
| 237 | sourceId: input.source.id, | ||
| 238 | catalogId, | ||
| 239 | generatedAt, | ||
| 240 | revision, | ||
| 241 | snapshot, | ||
| 242 | }; | ||
| 243 | }; | ||
| 244 | |||
| 245 | export const readLocalSourceArtifact = (input: { | ||
| 246 | context: ResolvedLocalWorkspaceContext; | ||
| 247 | sourceId: string; | ||
| 248 | }): Effect.Effect< | ||
| 249 | LocalSourceArtifact | null, | ||
| 250 | LocalFileSystemError, | ||
| 251 | FileSystem.FileSystem | ||
| 252 | > => | ||
| 253 | Effect.gen(function* () { | ||
| 254 | const fs = yield* FileSystem.FileSystem; | ||
| 255 | const path = localSourceArtifactPath(input); | ||
| 256 | const exists = yield* fs.exists(path).pipe( | ||
| 257 | Effect.mapError(mapFileSystemError(path, "check source artifact path")), | ||
| 258 | ); | ||
| 259 | if (!exists) { | ||
| 260 | return null; | ||
| 261 | } | ||
| 262 | |||
| 263 | const readPath = path; | ||
| 264 | const content = yield* fs.readFileString(readPath, "utf8").pipe( | ||
| 265 | Effect.mapError(mapFileSystemError(readPath, "read source artifact")), | ||
| 266 | ); | ||
| 267 | |||
| 268 | const decodedArtifact = decodeReadableLocalSourceArtifactOption(content); | ||
| 269 | if (Option.isNone(decodedArtifact)) { | ||
| 270 | return null; | ||
| 271 | } | ||
| 272 | |||
| 273 | const artifact = normalizeLocalSourceArtifact(decodedArtifact.value); | ||
| 274 | const snapshot = decodeCatalogSnapshotV1Option(artifact.snapshot); | ||
| 275 | if (snapshot === null) { | ||
| 276 | return null; | ||
| 277 | } | ||
| 278 | |||
| 279 | const decodedSnapshotArtifact: LocalSourceArtifact = { | ||
| 280 | ...artifact, | ||
| 281 | snapshot, | ||
| 282 | }; | ||
| 283 | |||
| 284 | const rawDocuments: Record<string, NativeBlob> = {}; | ||
| 285 | for (const documentId of Object.keys(decodedSnapshotArtifact.snapshot.catalog.documents)) { | ||
| 286 | const sourceDocumentPath = localSourceDocumentPath({ | ||
| 287 | context: input.context, | ||
| 288 | sourceId: input.sourceId, | ||
| 289 | documentId, | ||
| 290 | }); | ||
| 291 | const sourceDocumentExists = yield* fs.exists(sourceDocumentPath).pipe( | ||
| 292 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "check source document path")), | ||
| 293 | ); | ||
| 294 | if (!sourceDocumentExists) { | ||
| 295 | continue; | ||
| 296 | } | ||
| 297 | |||
| 298 | const sourceContent = yield* fs.readFileString(sourceDocumentPath, "utf8").pipe( | ||
| 299 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "read source document")), | ||
| 300 | ); | ||
| 301 | rawDocuments[documentId] = { | ||
| 302 | sourceKind: decodedSnapshotArtifact.snapshot.import.sourceKind, | ||
| 303 | kind: "source_document", | ||
| 304 | value: sourceContent, | ||
| 305 | } satisfies NativeBlob; | ||
| 306 | } | ||
| 307 | |||
| 308 | const hydratedArtifact = Object.keys(rawDocuments).length > 0 | ||
| 309 | ? hydrateArtifactSourceDocuments({ | ||
| 310 | artifact: decodedSnapshotArtifact, | ||
| 311 | rawDocuments, | ||
| 312 | }) | ||
| 313 | : decodedSnapshotArtifact; | ||
| 314 | |||
| 315 | return { | ||
| 316 | ...hydratedArtifact, | ||
| 317 | snapshot: hydratedArtifact.snapshot, | ||
| 318 | }; | ||
| 319 | }); | ||
| 320 | |||
| 321 | export const writeLocalSourceArtifact = (input: { | ||
| 322 | context: ResolvedLocalWorkspaceContext; | ||
| 323 | sourceId: string; | ||
| 324 | artifact: LocalSourceArtifact; | ||
| 325 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 326 | Effect.gen(function* () { | ||
| 327 | const fs = yield* FileSystem.FileSystem; | ||
| 328 | const directory = join(input.context.artifactsDirectory, "sources"); | ||
| 329 | const path = localSourceArtifactPath(input); | ||
| 330 | const sourceDocumentDirectory = localSourceDocumentDirectory(input); | ||
| 331 | const split = splitArtifactSourceDocuments(input.artifact); | ||
| 332 | yield* fs.makeDirectory(directory, { recursive: true }).pipe( | ||
| 333 | Effect.mapError(mapFileSystemError(directory, "create source artifact directory")), | ||
| 334 | ); | ||
| 335 | yield* fs.remove(sourceDocumentDirectory, { recursive: true, force: true }).pipe( | ||
| 336 | Effect.mapError(mapFileSystemError(sourceDocumentDirectory, "remove source document directory")), | ||
| 337 | ); | ||
| 338 | if (split.rawDocuments.length > 0) { | ||
| 339 | yield* fs.makeDirectory(sourceDocumentDirectory, { recursive: true }).pipe( | ||
| 340 | Effect.mapError(mapFileSystemError(sourceDocumentDirectory, "create source document directory")), | ||
| 341 | ); | ||
| 342 | for (const rawDocument of split.rawDocuments) { | ||
| 343 | const sourceDocumentPath = localSourceDocumentPath({ | ||
| 344 | context: input.context, | ||
| 345 | sourceId: input.sourceId, | ||
| 346 | documentId: rawDocument.documentId, | ||
| 347 | }); | ||
| 348 | yield* fs.writeFileString(sourceDocumentPath, rawDocument.content).pipe( | ||
| 349 | Effect.mapError(mapFileSystemError(sourceDocumentPath, "write source document")), | ||
| 350 | ); | ||
| 351 | } | ||
| 352 | } | ||
| 353 | yield* fs.writeFileString(path, `${JSON.stringify(split.artifact)}\n`).pipe( | ||
| 354 | Effect.mapError(mapFileSystemError(path, "write source artifact")), | ||
| 355 | ); | ||
| 356 | }); | ||
| 357 | |||
| 358 | export const removeLocalSourceArtifact = (input: { | ||
| 359 | context: ResolvedLocalWorkspaceContext; | ||
| 360 | sourceId: string; | ||
| 361 | }): Effect.Effect<void, LocalFileSystemError, FileSystem.FileSystem> => | ||
| 362 | Effect.gen(function* () { | ||
| 363 | const fs = yield* FileSystem.FileSystem; | ||
| 364 | const path = localSourceArtifactPath(input); | ||
| 365 | const exists = yield* fs.exists(path).pipe( | ||
| 366 | Effect.mapError(mapFileSystemError(path, "check source artifact path")), | ||
| 367 | ); | ||
| 368 | if (exists) { | ||
| 369 | yield* fs.remove(path).pipe( | ||
| 370 | Effect.mapError(mapFileSystemError(path, "remove source artifact")), | ||
| 371 | ); | ||
| 372 | } | ||
| 373 | const sourceDocumentDirectory = localSourceDocumentDirectory(input); | ||
| 374 | yield* fs.remove(sourceDocumentDirectory, { recursive: true, force: true }).pipe( | ||
| 375 | Effect.mapError(mapFileSystemError(sourceDocumentDirectory, "remove source document directory")), | ||
| 376 | ); | ||
| 377 | }); | ||
| @@ -1,225 +0,0 @@ | |||
| 1 | import { FileSystem } from "@effect/platform"; | ||
| 2 | import { NodeFileSystem } from "@effect/platform-node"; | ||
| 3 | import type { LocalInstallation, LocalExecutorConfig } from "#schema"; | ||
| 4 | import * as Context from "effect/Context"; | ||
| 5 | import * as Effect from "effect/Effect"; | ||
| 6 | import * as Layer from "effect/Layer"; | ||
| 7 | |||
| 8 | import type { | ||
| 9 | LoadedLocalExecutorConfig, | ||
| 10 | ResolvedLocalWorkspaceContext, | ||
| 11 | } from "./config"; | ||
| 12 | import { | ||
| 13 | loadLocalExecutorConfig, | ||
| 14 | resolveConfigRelativePath, | ||
| 15 | writeProjectLocalExecutorConfig, | ||
| 16 | } from "./config"; | ||
| 17 | import { | ||
| 18 | getOrProvisionLocalInstallation, | ||
| 19 | loadLocalInstallation, | ||
| 20 | } from "./installation"; | ||
| 21 | import type { | ||
| 22 | LocalSourceArtifact, | ||
| 23 | } from "./source-artifacts"; | ||
| 24 | import { | ||
| 25 | buildLocalSourceArtifact, | ||
| 26 | readLocalSourceArtifact, | ||
| 27 | removeLocalSourceArtifact, | ||
| 28 | writeLocalSourceArtifact, | ||
| 29 | } from "./source-artifacts"; | ||
| 30 | import type { LocalWorkspaceState } from "./workspace-state"; | ||
| 31 | import { | ||
| 32 | loadLocalWorkspaceState, | ||
| 33 | writeLocalWorkspaceState, | ||
| 34 | } from "./workspace-state"; | ||
| 35 | import type { SourceCatalogSyncResult } from "@executor/source-core"; | ||
| 36 | import type { Source } from "#schema"; | ||
| 37 | |||
| 38 | export type InstallationStoreShape = { | ||
| 39 | load: ( | ||
| 40 | context: ResolvedLocalWorkspaceContext, | ||
| 41 | ) => Effect.Effect<LocalInstallation, Error, never>; | ||
| 42 | getOrProvision: (input: { | ||
| 43 | context: ResolvedLocalWorkspaceContext; | ||
| 44 | }) => Effect.Effect<LocalInstallation, Error, never>; | ||
| 45 | }; | ||
| 46 | |||
| 47 | export class InstallationStore extends Context.Tag( | ||
| 48 | "#runtime/InstallationStore", | ||
| 49 | )<InstallationStore, InstallationStoreShape>() {} | ||
| 50 | |||
| 51 | export type WorkspaceConfigStoreShape = { | ||
| 52 | load: ( | ||
| 53 | context: ResolvedLocalWorkspaceContext, | ||
| 54 | ) => Effect.Effect<LoadedLocalExecutorConfig, Error, never>; | ||
| 55 | writeProject: (input: { | ||
| 56 | context: ResolvedLocalWorkspaceContext; | ||
| 57 | config: LocalExecutorConfig; | ||
| 58 | }) => Effect.Effect<void, Error, never>; | ||
| 59 | resolveRelativePath: (input: { path: string; workspaceRoot: string }) => string; | ||
| 60 | }; | ||
| 61 | |||
| 62 | export class WorkspaceConfigStore extends Context.Tag( | ||
| 63 | "#runtime/WorkspaceConfigStore", | ||
| 64 | )<WorkspaceConfigStore, WorkspaceConfigStoreShape>() {} | ||
| 65 | |||
| 66 | export type WorkspaceStateStoreShape = { | ||
| 67 | load: ( | ||
| 68 | context: ResolvedLocalWorkspaceContext, | ||
| 69 | ) => Effect.Effect<LocalWorkspaceState, Error, never>; | ||
| 70 | write: (input: { | ||
| 71 | context: ResolvedLocalWorkspaceContext; | ||
| 72 | state: LocalWorkspaceState; | ||
| 73 | }) => Effect.Effect<void, Error, never>; | ||
| 74 | }; | ||
| 75 | |||
| 76 | export class WorkspaceStateStore extends Context.Tag( | ||
| 77 | "#runtime/WorkspaceStateStore", | ||
| 78 | )<WorkspaceStateStore, WorkspaceStateStoreShape>() {} | ||
| 79 | |||
| 80 | export type SourceArtifactStoreShape = { | ||
| 81 | build: (input: { | ||
| 82 | source: Source; | ||
| 83 | syncResult: SourceCatalogSyncResult; | ||
| 84 | }) => LocalSourceArtifact; | ||
| 85 | read: (input: { | ||
| 86 | context: ResolvedLocalWorkspaceContext; | ||
| 87 | sourceId: string; | ||
| 88 | }) => Effect.Effect<LocalSourceArtifact | null, Error, never>; | ||
| 89 | write: (input: { | ||
| 90 | context: ResolvedLocalWorkspaceContext; | ||
| 91 | sourceId: string; | ||
| 92 | artifact: LocalSourceArtifact; | ||
| 93 | }) => Effect.Effect<void, Error, never>; | ||
| 94 | remove: (input: { | ||
| 95 | context: ResolvedLocalWorkspaceContext; | ||
| 96 | sourceId: string; | ||
| 97 | }) => Effect.Effect<void, Error, never>; | ||
| 98 | }; | ||
| 99 | |||
| 100 | export class SourceArtifactStore extends Context.Tag( | ||
| 101 | "#runtime/SourceArtifactStore", | ||
| 102 | )<SourceArtifactStore, SourceArtifactStoreShape>() {} | ||
| 103 | |||
| 104 | export type LocalStorageServices = | ||
| 105 | | InstallationStore | ||
| 106 | | WorkspaceConfigStore | ||
| 107 | | WorkspaceStateStore | ||
| 108 | | SourceArtifactStore; | ||
| 109 | |||
| 110 | export type WorkspaceStorageServices = | ||
| 111 | | WorkspaceConfigStore | ||
| 112 | | WorkspaceStateStore | ||
| 113 | | SourceArtifactStore; | ||
| 114 | |||
| 115 | export const LocalInstallationStore: InstallationStoreShape = { | ||
| 116 | load: loadLocalInstallation, | ||
| 117 | getOrProvision: getOrProvisionLocalInstallation, | ||
| 118 | }; | ||
| 119 | |||
| 120 | export const LocalInstallationStoreLive = Layer.succeed( | ||
| 121 | InstallationStore, | ||
| 122 | LocalInstallationStore, | ||
| 123 | ); | ||
| 124 | |||
| 125 | const bindFileSystem = <A, E>( | ||
| 126 | fileSystem: FileSystem.FileSystem, | ||
| 127 | effect: Effect.Effect<A, E, FileSystem.FileSystem>, | ||
| 128 | ): Effect.Effect<A, E, never> => | ||
| 129 | effect.pipe(Effect.provideService(FileSystem.FileSystem, fileSystem)); | ||
| 130 | |||
| 131 | const bindNodeFileSystem = <A, E>( | ||
| 132 | effect: Effect.Effect<A, E, FileSystem.FileSystem>, | ||
| 133 | ): Effect.Effect<A, E, never> => | ||
| 134 | effect.pipe(Effect.provide(NodeFileSystem.layer)); | ||
| 135 | |||
| 136 | export const LocalWorkspaceConfigStore: WorkspaceConfigStoreShape = { | ||
| 137 | load: (context) => bindNodeFileSystem(loadLocalExecutorConfig(context)), | ||
| 138 | writeProject: (input) => bindNodeFileSystem(writeProjectLocalExecutorConfig(input)), | ||
| 139 | resolveRelativePath: resolveConfigRelativePath, | ||
| 140 | }; | ||
| 141 | |||
| 142 | export const LocalWorkspaceConfigStoreLive = Layer.effect( | ||
| 143 | WorkspaceConfigStore, | ||
| 144 | Effect.gen(function* () { | ||
| 145 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 146 | |||
| 147 | return WorkspaceConfigStore.of({ | ||
| 148 | load: (context) => bindFileSystem(fileSystem, loadLocalExecutorConfig(context)), | ||
| 149 | writeProject: (input) => | ||
| 150 | bindFileSystem(fileSystem, writeProjectLocalExecutorConfig(input)), | ||
| 151 | resolveRelativePath: resolveConfigRelativePath, | ||
| 152 | }); | ||
| 153 | }), | ||
| 154 | ); | ||
| 155 | |||
| 156 | export const LocalWorkspaceStateStore: WorkspaceStateStoreShape = { | ||
| 157 | load: (context) => bindNodeFileSystem(loadLocalWorkspaceState(context)), | ||
| 158 | write: (input) => bindNodeFileSystem(writeLocalWorkspaceState(input)), | ||
| 159 | }; | ||
| 160 | |||
| 161 | export const LocalWorkspaceStateStoreLive = Layer.effect( | ||
| 162 | WorkspaceStateStore, | ||
| 163 | Effect.gen(function* () { | ||
| 164 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 165 | |||
| 166 | return WorkspaceStateStore.of({ | ||
| 167 | load: (context) => bindFileSystem(fileSystem, loadLocalWorkspaceState(context)), | ||
| 168 | write: (input) => bindFileSystem(fileSystem, writeLocalWorkspaceState(input)), | ||
| 169 | }); | ||
| 170 | }), | ||
| 171 | ); | ||
| 172 | |||
| 173 | export const LocalSourceArtifactStore: SourceArtifactStoreShape = { | ||
| 174 | build: buildLocalSourceArtifact, | ||
| 175 | read: (input) => bindNodeFileSystem(readLocalSourceArtifact(input)), | ||
| 176 | write: (input) => bindNodeFileSystem(writeLocalSourceArtifact(input)), | ||
| 177 | remove: (input) => bindNodeFileSystem(removeLocalSourceArtifact(input)), | ||
| 178 | }; | ||
| 179 | |||
| 180 | export const LocalSourceArtifactStoreLive = Layer.effect( | ||
| 181 | SourceArtifactStore, | ||
| 182 | Effect.gen(function* () { | ||
| 183 | const fileSystem = yield* FileSystem.FileSystem; | ||
| 184 | |||
| 185 | return SourceArtifactStore.of({ | ||
| 186 | build: buildLocalSourceArtifact, | ||
| 187 | read: (input) => bindFileSystem(fileSystem, readLocalSourceArtifact(input)), | ||
| 188 | write: (input) => bindFileSystem(fileSystem, writeLocalSourceArtifact(input)), | ||
| 189 | remove: (input) => bindFileSystem(fileSystem, removeLocalSourceArtifact(input)), | ||
| 190 | }); | ||
| 191 | }), | ||
| 192 | ); | ||
| 193 | |||
| 194 | export const makeWorkspaceStorageLayer = (input: { | ||
| 195 | workspaceConfigStore: WorkspaceConfigStoreShape; | ||
| 196 | workspaceStateStore: WorkspaceStateStoreShape; | ||
| 197 | sourceArtifactStore: SourceArtifactStoreShape; | ||
| 198 | }) => | ||
| 199 | Layer.mergeAll( | ||
| 200 | Layer.succeed(WorkspaceConfigStore, input.workspaceConfigStore), | ||
| 201 | Layer.succeed(WorkspaceStateStore, input.workspaceStateStore), | ||
| 202 | Layer.succeed(SourceArtifactStore, input.sourceArtifactStore), | ||
| 203 | ); | ||
| 204 | |||
| 205 | export const makeLocalStorageLayer = (input: { | ||
| 206 | installationStore: InstallationStoreShape; | ||
| 207 | workspaceConfigStore: WorkspaceConfigStoreShape; | ||
| 208 | workspaceStateStore: WorkspaceStateStoreShape; | ||
| 209 | sourceArtifactStore: SourceArtifactStoreShape; | ||
| 210 | }) => | ||
| 211 | Layer.mergeAll( | ||
| 212 | Layer.succeed(InstallationStore, input.installationStore), | ||
| 213 | makeWorkspaceStorageLayer(input), | ||
| 214 | ); | ||
| 215 | |||
| 216 | export const WorkspaceStorageLive = Layer.mergeAll( | ||
| 217 | LocalWorkspaceConfigStoreLive, | ||
| 218 | LocalWorkspaceStateStoreLive, | ||
| 219 | LocalSourceArtifactStoreLive, | ||
| 220 | ); | ||
| 221 | |||
| 222 | export const LocalStorageLive = Layer.mergeAll( | ||
| 223 | LocalInstallationStoreLive, | ||
| 224 | WorkspaceStorageLive, | ||
| 225 | ); | ||
| @@ -1 +0,0 @@ | |||
| 1 | export * from "../../policies/operations"; | ||
| @@ -0,0 +1,234 @@ | |||
| 1 | import { | ||
| 2 | SourceIdSchema, | ||
| 3 | SourceCatalogIdSchema, | ||
| 4 | StoredSourceCatalogRevisionRecordSchema, | ||
| 5 | TimestampMsSchema, | ||
| 6 | type Source, | ||
| 7 | type SourceCatalogId, | ||
| 8 | } from "#schema"; | ||
| 9 | import * as Option from "effect/Option"; | ||
| 10 | import * as Schema from "effect/Schema"; | ||
| 11 | |||
| 12 | import { decodeCatalogSnapshotV1 } from "@executor/ir/catalog"; | ||
| 13 | import { | ||
| 14 | CatalogSnapshotV1Schema, | ||
| 15 | type CatalogSnapshotV1, | ||
| 16 | type NativeBlob, | ||
| 17 | type SourceDocument, | ||
| 18 | } from "@executor/ir/model"; | ||
| 19 | import { | ||
| 20 | contentHash, | ||
| 21 | snapshotFromSourceCatalogSyncResult, | ||
| 22 | type SourceCatalogSyncResult, | ||
| 23 | } from "@executor/source-core"; | ||
| 24 | import { | ||
| 25 | createSourceCatalogRevisionRecord, | ||
| 26 | stableSourceCatalogId, | ||
| 27 | } from "./sources/source-definitions"; | ||
| 28 | |||
| 29 | const LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION = 3 as const; | ||
| 30 | const LOCAL_SOURCE_ARTIFACT_VERSION = 4 as const; | ||
| 31 | |||
| 32 | const LegacyLocalSourceArtifactSchema = Schema.Struct({ | ||
| 33 | version: Schema.Literal(LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 34 | sourceId: SourceIdSchema, | ||
| 35 | catalogId: SourceCatalogIdSchema, | ||
| 36 | generatedAt: TimestampMsSchema, | ||
| 37 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 38 | snapshot: CatalogSnapshotV1Schema, | ||
| 39 | }); | ||
| 40 | |||
| 41 | export const LocalSourceArtifactSchema = Schema.Struct({ | ||
| 42 | version: Schema.Literal(LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 43 | sourceId: SourceIdSchema, | ||
| 44 | catalogId: SourceCatalogIdSchema, | ||
| 45 | generatedAt: TimestampMsSchema, | ||
| 46 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 47 | snapshot: CatalogSnapshotV1Schema, | ||
| 48 | }); | ||
| 49 | |||
| 50 | export type LocalSourceArtifact = typeof LocalSourceArtifactSchema.Type; | ||
| 51 | type LegacyLocalSourceArtifact = typeof LegacyLocalSourceArtifactSchema.Type; | ||
| 52 | |||
| 53 | const ReadableLegacyLocalSourceArtifactSchema = Schema.Struct({ | ||
| 54 | version: Schema.Literal(LEGACY_LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 55 | sourceId: SourceIdSchema, | ||
| 56 | catalogId: SourceCatalogIdSchema, | ||
| 57 | generatedAt: TimestampMsSchema, | ||
| 58 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 59 | snapshot: Schema.Unknown, | ||
| 60 | }); | ||
| 61 | |||
| 62 | const ReadableLocalSourceArtifactSchema = Schema.Struct({ | ||
| 63 | version: Schema.Literal(LOCAL_SOURCE_ARTIFACT_VERSION), | ||
| 64 | sourceId: SourceIdSchema, | ||
| 65 | catalogId: SourceCatalogIdSchema, | ||
| 66 | generatedAt: TimestampMsSchema, | ||
| 67 | revision: StoredSourceCatalogRevisionRecordSchema, | ||
| 68 | snapshot: Schema.Unknown, | ||
| 69 | }); | ||
| 70 | |||
| 71 | type ReadableLegacyLocalSourceArtifact = | ||
| 72 | typeof ReadableLegacyLocalSourceArtifactSchema.Type; | ||
| 73 | type ReadableLocalSourceArtifact = | ||
| 74 | typeof ReadableLocalSourceArtifactSchema.Type; | ||
| 75 | |||
| 76 | const decodeReadableLocalSourceArtifactOption = Schema.decodeUnknownOption( | ||
| 77 | Schema.parseJson( | ||
| 78 | Schema.Union( | ||
| 79 | ReadableLocalSourceArtifactSchema, | ||
| 80 | ReadableLegacyLocalSourceArtifactSchema, | ||
| 81 | ), | ||
| 82 | ), | ||
| 83 | ); | ||
| 84 | |||
| 85 | const normalizeLocalSourceArtifact = ( | ||
| 86 | artifact: ReadableLocalSourceArtifact | ReadableLegacyLocalSourceArtifact, | ||
| 87 | ): Omit<LocalSourceArtifact, "snapshot"> & { snapshot: unknown } => | ||
| 88 | artifact.version === LOCAL_SOURCE_ARTIFACT_VERSION | ||
| 89 | ? artifact | ||
| 90 | : { | ||
| 91 | ...artifact, | ||
| 92 | version: LOCAL_SOURCE_ARTIFACT_VERSION, | ||
| 93 | }; | ||
| 94 | |||
| 95 | const mutableRecord = <K extends string, V>( | ||
| 96 | value: Readonly<Record<K, V>>, | ||
| 97 | ): Record<K, V> => value as Record<K, V>; | ||
| 98 | |||
| 99 | export type SourceArtifactDocumentContent = { | ||
| 100 | documentId: string; | ||
| 101 | blob: NativeBlob; | ||
| 102 | content: string; | ||
| 103 | }; | ||
| 104 | |||
| 105 | export const splitLocalSourceArtifactDocuments = ( | ||
| 106 | artifact: LocalSourceArtifact, | ||
| 107 | ): { | ||
| 108 | artifact: LocalSourceArtifact; | ||
| 109 | rawDocuments: readonly SourceArtifactDocumentContent[]; | ||
| 110 | } => { | ||
| 111 | const nextArtifact = structuredClone(artifact); | ||
| 112 | const rawDocuments: Array<SourceArtifactDocumentContent> = []; | ||
| 113 | |||
| 114 | for (const [documentId, document] of Object.entries( | ||
| 115 | nextArtifact.snapshot.catalog.documents, | ||
| 116 | )) { | ||
| 117 | const mutableDocument = document as SourceDocument & { native?: NativeBlob[] }; | ||
| 118 | const sourceDocumentBlob = document.native?.find( | ||
| 119 | (blob) => blob.kind === "source_document" && typeof blob.value === "string", | ||
| 120 | ); | ||
| 121 | if (!sourceDocumentBlob || typeof sourceDocumentBlob.value !== "string") { | ||
| 122 | continue; | ||
| 123 | } | ||
| 124 | |||
| 125 | rawDocuments.push({ | ||
| 126 | documentId, | ||
| 127 | blob: sourceDocumentBlob, | ||
| 128 | content: sourceDocumentBlob.value, | ||
| 129 | }); | ||
| 130 | |||
| 131 | const remainingBlobs = (document.native ?? []).filter( | ||
| 132 | (blob) => blob !== sourceDocumentBlob, | ||
| 133 | ); | ||
| 134 | if (remainingBlobs.length > 0) { | ||
| 135 | mutableDocument.native = remainingBlobs; | ||
| 136 | } else { | ||
| 137 | delete mutableDocument.native; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | return { | ||
| 142 | artifact: nextArtifact, | ||
| 143 | rawDocuments, | ||
| 144 | }; | ||
| 145 | }; | ||
| 146 | |||
| 147 | export const hydrateLocalSourceArtifactDocuments = (input: { | ||
| 148 | artifact: LocalSourceArtifact; | ||
| 149 | rawDocuments: Readonly<Record<string, NativeBlob>>; | ||
| 150 | }): LocalSourceArtifact => { | ||
| 151 | const nextArtifact = structuredClone(input.artifact); | ||
| 152 | const mutableDocuments = mutableRecord(nextArtifact.snapshot.catalog.documents); | ||
| 153 | |||
| 154 | for (const [documentId, rawDocument] of Object.entries(input.rawDocuments)) { | ||
| 155 | const document = mutableDocuments[ | ||
| 156 | documentId as keyof typeof mutableDocuments | ||
| 157 | ] as (SourceDocument & { native?: NativeBlob[] }) | undefined; | ||
| 158 | if (!document) { | ||
| 159 | continue; | ||
| 160 | } | ||
| 161 | |||
| 162 | const remainingBlobs = (document.native ?? []).filter( | ||
| 163 | (blob) => blob.kind !== "source_document", | ||
| 164 | ); | ||
| 165 | document.native = [rawDocument, ...remainingBlobs]; | ||
| 166 | } | ||
| 167 | |||
| 168 | return nextArtifact; | ||
| 169 | }; | ||
| 170 | |||
| 171 | const snapshotHash = (snapshot: CatalogSnapshotV1): string => | ||
| 172 | contentHash(JSON.stringify(snapshot)); | ||
| 173 | |||
| 174 | const importMetadataHash = (snapshot: { | ||
| 175 | import: SourceCatalogSyncResult["importMetadata"]; | ||
| 176 | }): string => contentHash(JSON.stringify(snapshot.import)); | ||
| 177 | |||
| 178 | const decodeCatalogSnapshotV1Option = ( | ||
| 179 | snapshot: unknown, | ||
| 180 | ): CatalogSnapshotV1 | null => { | ||
| 181 | try { | ||
| 182 | return decodeCatalogSnapshotV1(snapshot); | ||
| 183 | } catch { | ||
| 184 | return null; | ||
| 185 | } | ||
| 186 | }; | ||
| 187 | |||
| 188 | export const decodeStoredLocalSourceArtifact = ( | ||
| 189 | content: string, | ||
| 190 | ): LocalSourceArtifact | null => { | ||
| 191 | const decodedArtifact = decodeReadableLocalSourceArtifactOption(content); | ||
| 192 | if (Option.isNone(decodedArtifact)) { | ||
| 193 | return null; | ||
| 194 | } | ||
| 195 | |||
| 196 | const artifact = normalizeLocalSourceArtifact(decodedArtifact.value); | ||
| 197 | const snapshot = decodeCatalogSnapshotV1Option(artifact.snapshot); | ||
| 198 | if (snapshot === null) { | ||
| 199 | return null; | ||
| 200 | } | ||
| 201 | |||
| 202 | return { | ||
| 203 | ...artifact, | ||
| 204 | snapshot, | ||
| 205 | }; | ||
| 206 | }; | ||
| 207 | |||
| 208 | export const buildLocalSourceArtifact = (input: { | ||
| 209 | source: Source; | ||
| 210 | syncResult: SourceCatalogSyncResult; | ||
| 211 | }): LocalSourceArtifact => { | ||
| 212 | const catalogId: SourceCatalogId = stableSourceCatalogId(input.source); | ||
| 213 | const generatedAt = Date.now(); | ||
| 214 | const snapshot = snapshotFromSourceCatalogSyncResult(input.syncResult); | ||
| 215 | const importHash = importMetadataHash(snapshot); | ||
| 216 | const hash = snapshotHash(snapshot); | ||
| 217 | const revision = createSourceCatalogRevisionRecord({ | ||
| 218 | source: input.source, | ||
| 219 | catalogId, | ||
| 220 | revisionNumber: 1, | ||
| 221 | importMetadataJson: JSON.stringify(snapshot.import), | ||
| 222 | importMetadataHash: importHash, | ||
| 223 | snapshotHash: hash, | ||
| 224 | }); | ||
| 225 | |||
| 226 | return { | ||
| 227 | version: LOCAL_SOURCE_ARTIFACT_VERSION, | ||
| 228 | sourceId: input.source.id, | ||
| 229 | catalogId, | ||
| 230 | generatedAt, | ||
| 231 | revision, | ||
| 232 | snapshot, | ||
| 233 | }; | ||
| 234 | }; | ||
| @@ -19,7 +19,7 @@ import * as Effect from "effect/Effect"; · 18 unmodified lines | |||
| 19 | import * as Cause from "effect/Cause"; | 19 | import * as Cause from "effect/Cause"; |
| 20 | import * as Exit from "effect/Exit"; | 20 | import * as Exit from "effect/Exit"; |
| 21 | import * as Schema from "effect/Schema"; | 21 | import * as Schema from "effect/Schema"; |
| 22 | import type { RuntimeLocalWorkspaceState } from "../local/runtime-context"; | 22 | import type { RuntimeLocalWorkspaceState } from "../workspace/runtime-context"; |
| 23 | import { | 23 | import { |
| 24 | type LocalStorageServices, | 24 | type LocalStorageServices, |
| 25 | type InstallationStoreShape, | 25 | type InstallationStoreShape, |
| @@ -27,8 +27,8 @@ import { · 1 unmodified line | |||
| 27 | type WorkspaceConfigStoreShape, | 27 | type WorkspaceConfigStoreShape, |
| 28 | type WorkspaceStateStoreShape, | 28 | type WorkspaceStateStoreShape, |
| 29 | makeLocalStorageLayer, | 29 | makeLocalStorageLayer, |
| 30 | } from "../local/storage"; | 30 | } from "../workspace/storage"; |
| 31 | import { provideOptionalRuntimeLocalWorkspace } from "../local/runtime-context"; | 31 | import { provideOptionalRuntimeLocalWorkspace } from "../workspace/runtime-context"; |
| 32 | import { runtimeEffectError } from "../effect-errors"; | 32 | import { runtimeEffectError } from "../effect-errors"; |
| 33 | 33 | ||
| 34 | /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */ | 34 | /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */ |
| @@ -54,7 +54,7 @@ import { · 53 unmodified lines | |||
| 54 | getRuntimeLocalWorkspaceOption, | 54 | getRuntimeLocalWorkspaceOption, |
| 55 | provideOptionalRuntimeLocalWorkspace, | 55 | provideOptionalRuntimeLocalWorkspace, |
| 56 | type RuntimeLocalWorkspaceState, | 56 | type RuntimeLocalWorkspaceState, |
| 57 | } from "../local/runtime-context"; | 57 | } from "../workspace/runtime-context"; |
| 58 | import { | 58 | import { |
| 59 | exchangeMcpOAuthAuthorizationCode, | 59 | exchangeMcpOAuthAuthorizationCode, |
| 60 | startMcpOAuthAuthorization, | 60 | startMcpOAuthAuthorization, |
| @@ -73,12 +73,13 @@ import { · 12 unmodified lines | |||
| 73 | sourceBindingStateFromSource, | 73 | sourceBindingStateFromSource, |
| 74 | } from "./source-adapters"; | 74 | } from "./source-adapters"; |
| 75 | import { | 75 | import { |
| 76 | createDefaultSecretMaterialDeleter, | 76 | type DeleteSecretMaterial, |
| 77 | type ResolveSecretMaterial, | 77 | type ResolveSecretMaterial, |
| 78 | SecretMaterialDeleterService, | ||
| 78 | SecretMaterialResolverService, | 79 | SecretMaterialResolverService, |
| 79 | SecretMaterialStorerService, | 80 | SecretMaterialStorerService, |
| 80 | type StoreSecretMaterial, | 81 | type StoreSecretMaterial, |
| 81 | } from "../local/secret-material-providers"; | 82 | } from "../workspace/secret-material-providers"; |
| 82 | import { | 83 | import { |
| 83 | removeAuthLeaseAndSecrets, | 84 | removeAuthLeaseAndSecrets, |
| 84 | upsertOauth2AuthorizedUserLeaseFromTokenResponse, | 85 | upsertOauth2AuthorizedUserLeaseFromTokenResponse, |
| @@ -101,7 +102,7 @@ import { · 16 unmodified lines | |||
| 101 | type RuntimeSourceStore, | 102 | type RuntimeSourceStore, |
| 102 | RuntimeSourceStoreService, | 103 | RuntimeSourceStoreService, |
| 103 | } from "./source-store"; | 104 | } from "./source-store"; |
| 104 | import type { WorkspaceStorageServices } from "../local/storage"; | 105 | import type { WorkspaceStorageServices } from "../workspace/storage"; |
| 105 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; | 106 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; |
| 106 | import { runtimeEffectError } from "../effect-errors"; | 107 | import { runtimeEffectError } from "../effect-errors"; |
| 107 | 108 | ||
| @@ -994,6 +995,7 @@ const upsertSourceOauthClient = (input: { · 886 unmodified lines | |||
| 994 | source: Source; | 995 | source: Source; |
| 995 | oauthClient: SourceOauthClientInput; | 996 | oauthClient: SourceOauthClientInput; |
| 996 | storeSecretMaterial: StoreSecretMaterial; | 997 | storeSecretMaterial: StoreSecretMaterial; |
| 998 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 997 | }): Effect.Effect<ResolvedSourceOauthClient, Error, never> => | 999 | }): Effect.Effect<ResolvedSourceOauthClient, Error, never> => |
| 998 | Effect.gen(function* () { | 1000 | Effect.gen(function* () { |
| 999 | const adapter = getSourceAdapterForSource(input.source); | 1001 | const adapter = getSourceAdapterForSource(input.source); |
| @@ -1052,10 +1054,7 @@ const upsertSourceOauthClient = (input: { · 52 unmodified lines | |||
| 1052 | || previousClientSecretRef.handle !== clientSecretRef.handle | 1054 | || previousClientSecretRef.handle !== clientSecretRef.handle |
| 1053 | ) | 1055 | ) |
| 1054 | ) { | 1056 | ) { |
| 1055 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | 1057 | yield* input.deleteSecretMaterial(previousClientSecretRef).pipe( |
| 1056 | rows: input.rows, | ||
| 1057 | }); | ||
| 1058 | yield* deleteSecretMaterial(previousClientSecretRef).pipe( | ||
| 1059 | Effect.either, | 1058 | Effect.either, |
| 1060 | Effect.ignore, | 1059 | Effect.ignore, |
| 1061 | ); | 1060 | ); |
| @@ -1308,6 +1307,7 @@ const upsertProviderAuthGrant = (input: { · 246 unmodified lines | |||
| 1308 | grantedScopes: ReadonlyArray<string>; | 1307 | grantedScopes: ReadonlyArray<string>; |
| 1309 | refreshToken: string | null; | 1308 | refreshToken: string | null; |
| 1310 | storeSecretMaterial: StoreSecretMaterial; | 1309 | storeSecretMaterial: StoreSecretMaterial; |
| 1310 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 1311 | existingGrant?: import("#schema").ProviderAuthGrant | null; | 1311 | existingGrant?: import("#schema").ProviderAuthGrant | null; |
| 1312 | }): Effect.Effect<import("#schema").ProviderAuthGrant, Error, never> => | 1312 | }): Effect.Effect<import("#schema").ProviderAuthGrant, Error, never> => |
| 1313 | Effect.gen(function* () { | 1313 | Effect.gen(function* () { |
| @@ -1356,10 +1356,7 @@ const upsertProviderAuthGrant = (input: { · 42 unmodified lines | |||
| 1356 | || existingGrant.refreshToken.handle !== refreshTokenRef.handle | 1356 | || existingGrant.refreshToken.handle !== refreshTokenRef.handle |
| 1357 | ) | 1357 | ) |
| 1358 | ) { | 1358 | ) { |
| 1359 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | 1359 | yield* input.deleteSecretMaterial(existingGrant.refreshToken).pipe( |
| 1360 | rows: input.rows, | ||
| 1361 | }); | ||
| 1362 | yield* deleteSecretMaterial(existingGrant.refreshToken).pipe( | ||
| 1363 | Effect.either, | 1360 | Effect.either, |
| 1364 | Effect.ignore, | 1361 | Effect.ignore, |
| 1365 | ); | 1362 | ); |
| @@ -1747,6 +1744,7 @@ const removeProviderAuthGrantInternal = (input: { · 381 unmodified lines | |||
| 1747 | sourceStore: RuntimeSourceStore; | 1744 | sourceStore: RuntimeSourceStore; |
| 1748 | workspaceId: WorkspaceId; | 1745 | workspaceId: WorkspaceId; |
| 1749 | grantId: ProviderAuthGrant["id"]; | 1746 | grantId: ProviderAuthGrant["id"]; |
| 1747 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 1750 | }): Effect.Effect<boolean, Error, WorkspaceStorageServices> => | 1748 | }): Effect.Effect<boolean, Error, WorkspaceStorageServices> => |
| 1751 | Effect.gen(function* () { | 1749 | Effect.gen(function* () { |
| 1752 | const grantOption = yield* input.rows.providerAuthGrants.getById(input.grantId); | 1750 | const grantOption = yield* input.rows.providerAuthGrants.getById(input.grantId); |
| @@ -1774,7 +1772,7 @@ const removeProviderAuthGrantInternal = (input: { · 21 unmodified lines | |||
| 1774 | if (latestSource === null) { | 1772 | if (latestSource === null) { |
| 1775 | yield* removeAuthLeaseAndSecrets(input.rows, { | 1773 | yield* removeAuthLeaseAndSecrets(input.rows, { |
| 1776 | authArtifactId: artifact.id, | 1774 | authArtifactId: artifact.id, |
| 1777 | }); | 1775 | }, input.deleteSecretMaterial); |
| 1778 | yield* input.rows.authArtifacts.removeByWorkspaceSourceAndActor({ | 1776 | yield* input.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 1779 | workspaceId: artifact.workspaceId, | 1777 | workspaceId: artifact.workspaceId, |
| 1780 | sourceId: artifact.sourceId, | 1778 | sourceId: artifact.sourceId, |
| @@ -1800,7 +1798,7 @@ const removeProviderAuthGrantInternal = (input: { · 19 unmodified lines | |||
| 1800 | 1798 | ||
| 1801 | yield* removeProviderAuthGrantSecret(input.rows, { | 1799 | yield* removeProviderAuthGrantSecret(input.rows, { |
| 1802 | grant: grantOption.value, | 1800 | grant: grantOption.value, |
| 1803 | }); | 1801 | }, input.deleteSecretMaterial); |
| 1804 | yield* input.rows.providerAuthGrants.removeById(input.grantId); | 1802 | yield* input.rows.providerAuthGrants.removeById(input.grantId); |
| 1805 | return true; | 1803 | return true; |
| 1806 | }); | 1804 | }); |
| @@ -2093,6 +2091,7 @@ const addExecutorHttpSource = (input: { · 286 unmodified lines | |||
| 2093 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; | 2091 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2094 | sourceInput: ExecutorHttpEndpointSourceInput; | 2092 | sourceInput: ExecutorHttpEndpointSourceInput; |
| 2095 | storeSecretMaterial: StoreSecretMaterial; | 2093 | storeSecretMaterial: StoreSecretMaterial; |
| 2094 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 2096 | resolveSecretMaterial: ResolveSecretMaterial; | 2095 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2097 | getLocalServerBaseUrl?: () => string | undefined; | 2096 | getLocalServerBaseUrl?: () => string | undefined; |
| 2098 | baseUrl?: string | null; | 2097 | baseUrl?: string | null; |
| @@ -2305,6 +2304,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 206 unmodified lines | |||
| 2305 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; | 2304 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2306 | sourceInput: Extract<ExecutorAddSourceInput, { kind: "google_discovery" }>; | 2305 | sourceInput: Extract<ExecutorAddSourceInput, { kind: "google_discovery" }>; |
| 2307 | storeSecretMaterial: StoreSecretMaterial; | 2306 | storeSecretMaterial: StoreSecretMaterial; |
| 2307 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 2308 | resolveSecretMaterial: ResolveSecretMaterial; | 2308 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2309 | getLocalServerBaseUrl?: () => string | undefined; | 2309 | getLocalServerBaseUrl?: () => string | undefined; |
| 2310 | baseUrl?: string | null; | 2310 | baseUrl?: string | null; |
| @@ -2491,6 +2491,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 180 unmodified lines | |||
| 2491 | source: persistedDraft, | 2491 | source: persistedDraft, |
| 2492 | oauthClient: input.sourceInput.oauthClient, | 2492 | oauthClient: input.sourceInput.oauthClient, |
| 2493 | storeSecretMaterial: input.storeSecretMaterial, | 2493 | storeSecretMaterial: input.storeSecretMaterial, |
| 2494 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 2494 | }); | 2495 | }); |
| 2495 | } | 2496 | } |
| 2496 | 2497 | ||
| @@ -2820,6 +2821,7 @@ type RuntimeSourceAuthDependencies = { · 323 unmodified lines | |||
| 2820 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; | 2821 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2821 | resolveSecretMaterial: ResolveSecretMaterial; | 2822 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2822 | storeSecretMaterial: StoreSecretMaterial; | 2823 | storeSecretMaterial: StoreSecretMaterial; |
| 2824 | deleteSecretMaterial: DeleteSecretMaterial; | ||
| 2823 | getLocalServerBaseUrl?: () => string | undefined; | 2825 | getLocalServerBaseUrl?: () => string | undefined; |
| 2824 | localWorkspaceState?: RuntimeLocalWorkspaceState; | 2826 | localWorkspaceState?: RuntimeLocalWorkspaceState; |
| 2825 | }; | 2827 | }; |
| @@ -2880,6 +2882,7 @@ const createRuntimeSourceConnectionService = ( · 54 unmodified lines | |||
| 2880 | sourceCatalogSync: input.sourceCatalogSync, | 2882 | sourceCatalogSync: input.sourceCatalogSync, |
| 2881 | sourceInput, | 2883 | sourceInput, |
| 2882 | storeSecretMaterial: input.storeSecretMaterial, | 2884 | storeSecretMaterial: input.storeSecretMaterial, |
| 2885 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 2883 | resolveSecretMaterial: input.resolveSecretMaterial, | 2886 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 2884 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, | 2887 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 2885 | baseUrl: options?.baseUrl, | 2888 | baseUrl: options?.baseUrl, |
| @@ -2891,6 +2894,7 @@ const createRuntimeSourceConnectionService = ( · 5 unmodified lines | |||
| 2891 | sourceCatalogSync: input.sourceCatalogSync, | 2894 | sourceCatalogSync: input.sourceCatalogSync, |
| 2892 | sourceInput, | 2895 | sourceInput, |
| 2893 | storeSecretMaterial: input.storeSecretMaterial, | 2896 | storeSecretMaterial: input.storeSecretMaterial, |
| 2897 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 2894 | resolveSecretMaterial: input.resolveSecretMaterial, | 2898 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 2895 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, | 2899 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 2896 | baseUrl: options?.baseUrl, | 2900 | baseUrl: options?.baseUrl, |
| @@ -3014,10 +3018,7 @@ const createRuntimeSourceConnectionService = ( · 117 unmodified lines | |||
| 3014 | const secretRef = sourceOauthClientSecretRef(oauthClient.value); | 3018 | const secretRef = sourceOauthClientSecretRef(oauthClient.value); |
| 3015 | const removed = yield* input.rows.workspaceOauthClients.removeById(oauthClientId); | 3019 | const removed = yield* input.rows.workspaceOauthClients.removeById(oauthClientId); |
| 3016 | if (removed && secretRef) { | 3020 | if (removed && secretRef) { |
| 3017 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | 3021 | yield* input.deleteSecretMaterial(secretRef).pipe( |
| 3018 | rows: input.rows, | ||
| 3019 | }); | ||
| 3020 | yield* deleteSecretMaterial(secretRef).pipe( | ||
| 3021 | Effect.either, | 3022 | Effect.either, |
| 3022 | Effect.ignore, | 3023 | Effect.ignore, |
| 3023 | ); | 3024 | ); |
| @@ -3034,6 +3035,7 @@ const createRuntimeSourceConnectionService = ( · 10 unmodified lines | |||
| 3034 | sourceStore: input.sourceStore, | 3035 | sourceStore: input.sourceStore, |
| 3035 | workspaceId, | 3036 | workspaceId, |
| 3036 | grantId, | 3037 | grantId, |
| 3038 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 3037 | }), | 3039 | }), |
| 3038 | ), | 3040 | ), |
| 3039 | }; | 3041 | }; |
| @@ -3329,6 +3331,7 @@ const createRuntimeSourceOAuthSessionService = ( · 289 unmodified lines | |||
| 3329 | refreshToken: trimOrNull(exchanged.refresh_token), | 3331 | refreshToken: trimOrNull(exchanged.refresh_token), |
| 3330 | existingGrant, | 3332 | existingGrant, |
| 3331 | storeSecretMaterial: input.storeSecretMaterial, | 3333 | storeSecretMaterial: input.storeSecretMaterial, |
| 3334 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 3332 | }); | 3335 | }); |
| 3333 | 3336 | ||
| 3334 | const targets = yield* Effect.forEach( | 3337 | const targets = yield* Effect.forEach( |
| @@ -3526,6 +3529,8 @@ const createRuntimeSourceOAuthSessionService = ( · 191 unmodified lines | |||
| 3526 | rows: input.rows, | 3529 | rows: input.rows, |
| 3527 | artifact: authArtifact.value, | 3530 | artifact: authArtifact.value, |
| 3528 | tokenResponse: exchanged, | 3531 | tokenResponse: exchanged, |
| 3532 | storeSecretMaterial: input.storeSecretMaterial, | ||
| 3533 | deleteSecretMaterial: input.deleteSecretMaterial, | ||
| 3529 | }); | 3534 | }); |
| 3530 | } | 3535 | } |
| 3531 | 3536 | ||
| @@ -3698,6 +3703,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 166 unmodified lines | |||
| 3698 | const sourceCatalogSync = yield* RuntimeSourceCatalogSyncService; | 3703 | const sourceCatalogSync = yield* RuntimeSourceCatalogSyncService; |
| 3699 | const resolveSecretMaterial = yield* SecretMaterialResolverService; | 3704 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 3700 | const storeSecretMaterial = yield* SecretMaterialStorerService; | 3705 | const storeSecretMaterial = yield* SecretMaterialStorerService; |
| 3706 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 3701 | const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); | 3707 | const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); |
| 3702 | 3708 | ||
| 3703 | return createRuntimeSourceAuthService({ | 3709 | return createRuntimeSourceAuthService({ |
| @@ -3707,6 +3713,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 3 unmodified lines | |||
| 3707 | sourceCatalogSync, | 3713 | sourceCatalogSync, |
| 3708 | resolveSecretMaterial, | 3714 | resolveSecretMaterial, |
| 3709 | storeSecretMaterial, | 3715 | storeSecretMaterial, |
| 3716 | deleteSecretMaterial, | ||
| 3710 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, | 3717 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 3711 | localWorkspaceState: runtimeLocalWorkspace ?? undefined, | 3718 | localWorkspaceState: runtimeLocalWorkspace ?? undefined, |
| 3712 | }); | 3719 | }); |
| @@ -1 +0,0 @@ | |||
| 1 | export * from "../../sources/discovery"; | ||
| @@ -1 +0,0 @@ | |||
| 1 | export * from "../../sources/inspection"; | ||
| @@ -7,9 +7,9 @@ import { · 6 unmodified lines | |||
| 7 | type Source, | 7 | type Source, |
| 8 | } from "#schema"; | 8 | } from "#schema"; |
| 9 | 9 | ||
| 10 | import type { LoadedLocalExecutorConfig } from "../local/config"; | 10 | import type { LoadedLocalExecutorConfig } from "../workspace-config"; |
| 11 | import { buildLocalSourceRecord } from "./source-store"; | 11 | import { buildLocalSourceRecord } from "./source-store"; |
| 12 | import type { LocalWorkspaceState } from "../local/workspace-state"; | 12 | import type { LocalWorkspaceState } from "../workspace-state"; |
| 13 | 13 | ||
| 14 | const workspaceId = WorkspaceIdSchema.make("ws_source_store"); | 14 | const workspaceId = WorkspaceIdSchema.make("ws_source_store"); |
| 15 | const sourceId = SourceIdSchema.make("linear"); | 15 | const sourceId = SourceIdSchema.make("linear"); |
| @@ -7,13 +7,15 @@ import * as Context from "effect/Context"; · 6 unmodified lines | |||
| 7 | import * as Effect from "effect/Effect"; | 7 | import * as Effect from "effect/Effect"; |
| 8 | import * as Layer from "effect/Layer"; | 8 | import * as Layer from "effect/Layer"; |
| 9 | 9 | ||
| 10 | import { SourceTypeDeclarationsRefresherService } from "../catalog/source/type-declarations"; | ||
| 10 | import { | 11 | import { |
| 11 | SourceArtifactStore, | 12 | SourceArtifactStore, |
| 12 | type WorkspaceStorageServices, | 13 | type WorkspaceStorageServices, |
| 13 | WorkspaceConfigStore, | 14 | WorkspaceConfigStore, |
| 14 | WorkspaceStateStore, | 15 | WorkspaceStateStore, |
| 15 | } from "../local/storage"; | 16 | } from "../workspace/storage"; |
| 16 | import { RuntimeLocalWorkspaceService } from "../local/runtime-context"; | 17 | import { SecretMaterialDeleterService } from "../workspace/secret-material-providers"; |
| 18 | import { RuntimeLocalWorkspaceService } from "../workspace/runtime-context"; | ||
| 17 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; | 19 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; |
| 18 | import { | 20 | import { |
| 19 | loadRuntimeSourceStoreDeps, | 21 | loadRuntimeSourceStoreDeps, |
| @@ -64,7 +66,11 @@ export const loadSourcesInWorkspace = ( · 44 unmodified lines | |||
| 64 | options: { | 66 | options: { |
| 65 | actorAccountId?: AccountId | null; | 67 | actorAccountId?: AccountId | null; |
| 66 | } = {}, | 68 | } = {}, |
| 67 | ): Effect.Effect<readonly Source[], Error, WorkspaceStorageServices> => | 69 | ): Effect.Effect< |
| 70 | readonly Source[], | ||
| 71 | Error, | ||
| 72 | WorkspaceStorageServices | SourceTypeDeclarationsRefresherService | ||
| 73 | > => | ||
| 68 | Effect.flatMap( | 74 | Effect.flatMap( |
| 69 | loadRuntimeSourceStoreDeps(rows, workspaceId), | 75 | loadRuntimeSourceStoreDeps(rows, workspaceId), |
| 70 | (deps) => loadSourcesInWorkspaceWithDeps(deps, workspaceId, options), | 76 | (deps) => loadSourcesInWorkspaceWithDeps(deps, workspaceId, options), |
| @@ -79,7 +85,7 @@ export const listLinkedSecretSourcesInWorkspace = ( · 8 unmodified lines | |||
| 79 | ): Effect.Effect< | 85 | ): Effect.Effect< |
| 80 | Map<string, Array<{ sourceId: string; sourceName: string }>>, | 86 | Map<string, Array<{ sourceId: string; sourceName: string }>>, |
| 81 | Error, | 87 | Error, |
| 82 | WorkspaceStorageServices | 88 | WorkspaceStorageServices | SourceTypeDeclarationsRefresherService |
| 83 | > => | 89 | > => |
| 84 | Effect.flatMap( | 90 | Effect.flatMap( |
| 85 | loadRuntimeSourceStoreDeps(rows, workspaceId), | 91 | loadRuntimeSourceStoreDeps(rows, workspaceId), |
| @@ -93,7 +99,11 @@ export const loadSourceById = ( · 7 unmodified lines | |||
| 93 | sourceId: Source["id"]; | 99 | sourceId: Source["id"]; |
| 94 | actorAccountId?: AccountId | null; | 100 | actorAccountId?: AccountId | null; |
| 95 | }, | 101 | }, |
| 96 | ): Effect.Effect<Source, Error, WorkspaceStorageServices> => | 102 | ): Effect.Effect< |
| 103 | Source, | ||
| 104 | Error, | ||
| 105 | WorkspaceStorageServices | SourceTypeDeclarationsRefresherService | ||
| 106 | > => | ||
| 97 | Effect.flatMap( | 107 | Effect.flatMap( |
| 98 | loadRuntimeSourceStoreDeps(rows, input.workspaceId), | 108 | loadRuntimeSourceStoreDeps(rows, input.workspaceId), |
| 99 | (deps) => loadSourceByIdWithDeps(deps, input), | 109 | (deps) => loadSourceByIdWithDeps(deps, input), |
| @@ -105,11 +115,18 @@ export const removeSourceById = ( · 5 unmodified lines | |||
| 105 | workspaceId: WorkspaceId; | 115 | workspaceId: WorkspaceId; |
| 106 | sourceId: Source["id"]; | 116 | sourceId: Source["id"]; |
| 107 | }, | 117 | }, |
| 108 | ): Effect.Effect<boolean, Error, WorkspaceStorageServices> => | 118 | ): Effect.Effect< |
| 109 | Effect.flatMap( | 119 | boolean, |
| 110 | loadRuntimeSourceStoreDeps(rows, input.workspaceId), | 120 | Error, |
| 111 | (deps) => removeSourceByIdWithDeps(deps, input), | 121 | | WorkspaceStorageServices |
| 112 | ); | 122 | | SourceTypeDeclarationsRefresherService |
| 123 | | SecretMaterialDeleterService | ||
| 124 | > => | ||
| 125 | Effect.gen(function* () { | ||
| 126 | const deps = yield* loadRuntimeSourceStoreDeps(rows, input.workspaceId); | ||
| 127 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 128 | return yield* removeSourceByIdWithDeps(deps, input, deleteSecretMaterial); | ||
| 129 | }); | ||
| 113 | 130 | ||
| 114 | export const persistSource = ( | 131 | export const persistSource = ( |
| 115 | rows: ControlPlaneStoreShape, | 132 | rows: ControlPlaneStoreShape, |
| @@ -117,11 +134,18 @@ export const persistSource = ( · 1 unmodified line | |||
| 117 | options: { | 134 | options: { |
| 118 | actorAccountId?: AccountId | null; | 135 | actorAccountId?: AccountId | null; |
| 119 | } = {}, | 136 | } = {}, |
| 120 | ): Effect.Effect<Source, Error, WorkspaceStorageServices> => | 137 | ): Effect.Effect< |
| 121 | Effect.flatMap( | 138 | Source, |
| 122 | loadRuntimeSourceStoreDeps(rows, source.workspaceId), | 139 | Error, |
| 123 | (deps) => persistSourceWithDeps(deps, source, options), | 140 | | WorkspaceStorageServices |
| 124 | ); | 141 | | SourceTypeDeclarationsRefresherService |
| 142 | | SecretMaterialDeleterService | ||
| 143 | > => | ||
| 144 | Effect.gen(function* () { | ||
| 145 | const deps = yield* loadRuntimeSourceStoreDeps(rows, source.workspaceId); | ||
| 146 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 147 | return yield* persistSourceWithDeps(deps, source, options, deleteSecretMaterial); | ||
| 148 | }); | ||
| 125 | 149 | ||
| 126 | export class RuntimeSourceStoreService extends Context.Tag( | 150 | export class RuntimeSourceStoreService extends Context.Tag( |
| 127 | "#runtime/RuntimeSourceStoreService", | 151 | "#runtime/RuntimeSourceStoreService", |
| @@ -135,6 +159,9 @@ export const RuntimeSourceStoreLive = Layer.effect( · 7 unmodified lines | |||
| 135 | const workspaceConfigStore = yield* WorkspaceConfigStore; | 159 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 136 | const workspaceStateStore = yield* WorkspaceStateStore; | 160 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 137 | const sourceArtifactStore = yield* SourceArtifactStore; | 161 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 162 | const sourceTypeDeclarationsRefresher = | ||
| 163 | yield* SourceTypeDeclarationsRefresherService; | ||
| 164 | const deleteSecretMaterial = yield* SecretMaterialDeleterService; | ||
| 138 | 165 | ||
| 139 | const deps: RuntimeSourceStoreDeps = { | 166 | const deps: RuntimeSourceStoreDeps = { |
| 140 | rows, | 167 | rows, |
| @@ -142,6 +169,7 @@ export const RuntimeSourceStoreLive = Layer.effect( · 1 unmodified line | |||
| 142 | workspaceConfigStore, | 169 | workspaceConfigStore, |
| 143 | workspaceStateStore, | 170 | workspaceStateStore, |
| 144 | sourceArtifactStore, | 171 | sourceArtifactStore, |
| 172 | sourceTypeDeclarationsRefresher, | ||
| 145 | }; | 173 | }; |
| 146 | 174 | ||
| 147 | return RuntimeSourceStoreService.of({ | 175 | return RuntimeSourceStoreService.of({ |
| @@ -152,9 +180,9 @@ export const RuntimeSourceStoreLive = Layer.effect( · 4 unmodified lines | |||
| 152 | loadSourceById: (input) => | 180 | loadSourceById: (input) => |
| 153 | loadSourceByIdWithDeps(deps, input), | 181 | loadSourceByIdWithDeps(deps, input), |
| 154 | removeSourceById: (input) => | 182 | removeSourceById: (input) => |
| 155 | removeSourceByIdWithDeps(deps, input), | 183 | removeSourceByIdWithDeps(deps, input, deleteSecretMaterial), |
| 156 | persistSource: (source, options = {}) => | 184 | persistSource: (source, options = {}) => |
| 157 | persistSourceWithDeps(deps, source, options), | 185 | persistSourceWithDeps(deps, source, options, deleteSecretMaterial), |
| 158 | }); | 186 | }); |
| 159 | }), | 187 | }), |
| 160 | ); | 188 | ); |
| @@ -11,7 +11,7 @@ import * as Effect from "effect/Effect"; · 10 unmodified lines | |||
| 11 | 11 | ||
| 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; | 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; |
| 13 | import { removeAuthLeaseAndSecrets } from "../../auth/auth-leases"; | 13 | import { removeAuthLeaseAndSecrets } from "../../auth/auth-leases"; |
| 14 | import { createDefaultSecretMaterialDeleter } from "../../local/secret-material-providers"; | 14 | import type { DeleteSecretMaterial } from "../../workspace/secret-material-providers"; |
| 15 | import type { ControlPlaneStoreShape } from "../../store"; | 15 | import type { ControlPlaneStoreShape } from "../../store"; |
| 16 | 16 | ||
| 17 | const secretRefKey = (ref: { providerId: string; handle: string }): string => | 17 | const secretRefKey = (ref: { providerId: string; handle: string }): string => |
| @@ -23,13 +23,12 @@ export const cleanupAuthArtifactSecretRefs = ( · 5 unmodified lines | |||
| 23 | previous: AuthArtifact | null; | 23 | previous: AuthArtifact | null; |
| 24 | next: AuthArtifact | null; | 24 | next: AuthArtifact | null; |
| 25 | }, | 25 | }, |
| 26 | deleteSecretMaterial: DeleteSecretMaterial, | ||
| 26 | ) => | 27 | ) => |
| 27 | Effect.gen(function* () { | 28 | Effect.gen(function* () { |
| 28 | if (input.previous === null) { | 29 | if (input.previous === null) { |
| 29 | return; | 30 | return; |
| 30 | } | 31 | } |
| 31 | |||
| 32 | const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | ||
| 33 | const nextRefKeys = new Set( | 32 | const nextRefKeys = new Set( |
| 34 | (input.next === null ? [] : authArtifactSecretMaterialRefs(input.next)).map( | 33 | (input.next === null ? [] : authArtifactSecretMaterialRefs(input.next)).map( |
| 35 | secretRefKey, | 34 | secretRefKey, |
| @@ -97,6 +96,7 @@ export const removeAuthArtifactsForSource = ( · 61 unmodified lines | |||
| 97 | workspaceId: WorkspaceId; | 96 | workspaceId: WorkspaceId; |
| 98 | sourceId: Source["id"]; | 97 | sourceId: Source["id"]; |
| 99 | }, | 98 | }, |
| 99 | deleteSecretMaterial: DeleteSecretMaterial, | ||
| 100 | ) => | 100 | ) => |
| 101 | Effect.gen(function* () { | 101 | Effect.gen(function* () { |
| 102 | const existingAuthArtifacts = yield* rows.authArtifacts.listByWorkspaceAndSourceId({ | 102 | const existingAuthArtifacts = yield* rows.authArtifacts.listByWorkspaceAndSourceId({ |
| @@ -114,7 +114,7 @@ export const removeAuthArtifactsForSource = ( · 11 unmodified lines | |||
| 114 | (artifact) => | 114 | (artifact) => |
| 115 | removeAuthLeaseAndSecrets(rows, { | 115 | removeAuthLeaseAndSecrets(rows, { |
| 116 | authArtifactId: artifact.id, | 116 | authArtifactId: artifact.id, |
| 117 | }), | 117 | }, deleteSecretMaterial), |
| 118 | { discard: true }, | 118 | { discard: true }, |
| 119 | ); | 119 | ); |
| 120 | 120 | ||
| @@ -124,7 +124,7 @@ export const removeAuthArtifactsForSource = ( · 3 unmodified lines | |||
| 124 | cleanupAuthArtifactSecretRefs(rows, { | 124 | cleanupAuthArtifactSecretRefs(rows, { |
| 125 | previous: artifact, | 125 | previous: artifact, |
| 126 | next: null, | 126 | next: null, |
| 127 | }), | 127 | }, deleteSecretMaterial), |
| 128 | { discard: true }, | 128 | { discard: true }, |
| 129 | ); | 129 | ); |
| 130 | 130 | ||
| @@ -6,12 +6,12 @@ import type { · 5 unmodified lines | |||
| 6 | } from "#schema"; | 6 | } from "#schema"; |
| 7 | import { SourceIdSchema } from "#schema"; | 7 | import { SourceIdSchema } from "#schema"; |
| 8 | 8 | ||
| 9 | import type { LoadedLocalExecutorConfig } from "../../local/config"; | 9 | import type { LoadedLocalExecutorConfig } from "../../workspace-config"; |
| 10 | import { LocalUnsupportedSourceKindError } from "../../local/errors"; | 10 | import { LocalUnsupportedSourceKindError } from "../../workspace-errors"; |
| 11 | import { | 11 | import { |
| 12 | fromConfigSecretProviderId, | 12 | fromConfigSecretProviderId, |
| 13 | toConfigSecretProviderId, | 13 | toConfigSecretProviderId, |
| 14 | } from "../../local/config-secrets"; | 14 | } from "../../workspace/config-secrets"; |
| 15 | import { getSourceAdapterForSource } from "../source-adapters"; | 15 | import { getSourceAdapterForSource } from "../source-adapters"; |
| 16 | import { slugify } from "../slug"; | 16 | import { slugify } from "../slug"; |
| 17 | 17 | ||
| @@ -1,33 +1,31 @@ | |||
| 1 | import type { AccountId, WorkspaceId } from "#schema"; | 1 | import type { AccountId, WorkspaceId } from "#schema"; |
| 2 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 3 | 3 | ||
| 4 | import type { | 4 | import type { LoadedLocalExecutorConfig } from "../../workspace-config"; |
| 5 | LoadedLocalExecutorConfig, | 5 | import { |
| 6 | ResolvedLocalWorkspaceContext, | 6 | SourceTypeDeclarationsRefresherService, |
| 7 | } from "../../local/config"; | 7 | type SourceTypeDeclarationsRefresherShape, |
| 8 | } from "../../catalog/source/type-declarations"; | ||
| 8 | import { | 9 | import { |
| 9 | LocalExecutorConfigDecodeError, | ||
| 10 | LocalFileSystemError, | ||
| 11 | LocalWorkspaceStateDecodeError, | ||
| 12 | RuntimeLocalWorkspaceMismatchError, | 10 | RuntimeLocalWorkspaceMismatchError, |
| 13 | RuntimeLocalWorkspaceUnavailableError, | 11 | RuntimeLocalWorkspaceUnavailableError, |
| 14 | } from "../../local/errors"; | 12 | } from "../../workspace-errors"; |
| 15 | import { | 13 | import { |
| 16 | requireRuntimeLocalWorkspace, | 14 | requireRuntimeLocalWorkspace, |
| 17 | type RuntimeLocalWorkspaceState, | 15 | type RuntimeLocalWorkspaceState, |
| 18 | } from "../../local/runtime-context"; | 16 | } from "../../workspace/runtime-context"; |
| 19 | import type { | 17 | import type { |
| 20 | SourceArtifactStoreShape, | 18 | SourceArtifactStoreShape, |
| 21 | WorkspaceStorageServices, | 19 | WorkspaceStorageServices, |
| 22 | WorkspaceConfigStoreShape, | 20 | WorkspaceConfigStoreShape, |
| 23 | WorkspaceStateStoreShape, | 21 | WorkspaceStateStoreShape, |
| 24 | } from "../../local/storage"; | 22 | } from "../../workspace/storage"; |
| 25 | import { | 23 | import { |
| 26 | SourceArtifactStore, | 24 | SourceArtifactStore, |
| 27 | WorkspaceConfigStore, | 25 | WorkspaceConfigStore, |
| 28 | WorkspaceStateStore, | 26 | WorkspaceStateStore, |
| 29 | } from "../../local/storage"; | 27 | } from "../../workspace/storage"; |
| 30 | import type { LocalWorkspaceState } from "../../local/workspace-state"; | 28 | import type { LocalWorkspaceState } from "../../workspace-state"; |
| 31 | import type { ControlPlaneStoreShape } from "../../store"; | 29 | import type { ControlPlaneStoreShape } from "../../store"; |
| 32 | 30 | ||
| 33 | export type RuntimeSourceStoreDeps = { | 31 | export type RuntimeSourceStoreDeps = { |
| @@ -36,10 +34,10 @@ export type RuntimeSourceStoreDeps = { · 2 unmodified lines | |||
| 36 | workspaceConfigStore: WorkspaceConfigStoreShape; | 34 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 37 | workspaceStateStore: WorkspaceStateStoreShape; | 35 | workspaceStateStore: WorkspaceStateStoreShape; |
| 38 | sourceArtifactStore: SourceArtifactStoreShape; | 36 | sourceArtifactStore: SourceArtifactStoreShape; |
| 37 | sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape; | ||
| 39 | }; | 38 | }; |
| 40 | 39 | ||
| 41 | export type ResolvedSourceStoreWorkspace = { | 40 | export type ResolvedSourceStoreWorkspace = { |
| 42 | context: ResolvedLocalWorkspaceContext; | ||
| 43 | installation: { | 41 | installation: { |
| 44 | workspaceId: WorkspaceId; | 42 | workspaceId: WorkspaceId; |
| 45 | accountId: AccountId; | 43 | accountId: AccountId; |
| @@ -51,6 +49,9 @@ export type ResolvedSourceStoreWorkspace = { · 5 unmodified lines | |||
| 51 | workspaceState: LocalWorkspaceState; | 49 | workspaceState: LocalWorkspaceState; |
| 52 | }; | 50 | }; |
| 53 | 51 | ||
| 52 | export type RuntimeSourceStoreServices = | ||
| 53 | WorkspaceStorageServices | SourceTypeDeclarationsRefresherService; | ||
| 54 | |||
| 54 | export const resolveRuntimeLocalWorkspaceFromDeps = ( | 55 | export const resolveRuntimeLocalWorkspaceFromDeps = ( |
| 55 | deps: RuntimeSourceStoreDeps, | 56 | deps: RuntimeSourceStoreDeps, |
| 56 | workspaceId: WorkspaceId, | 57 | workspaceId: WorkspaceId, |
| @@ -58,9 +59,6 @@ export const resolveRuntimeLocalWorkspaceFromDeps = ( · 1 unmodified line | |||
| 58 | ResolvedSourceStoreWorkspace, | 59 | ResolvedSourceStoreWorkspace, |
| 59 | | RuntimeLocalWorkspaceUnavailableError | 60 | | RuntimeLocalWorkspaceUnavailableError |
| 60 | | RuntimeLocalWorkspaceMismatchError | 61 | | RuntimeLocalWorkspaceMismatchError |
| 61 | | LocalFileSystemError | ||
| 62 | | LocalExecutorConfigDecodeError | ||
| 63 | | LocalWorkspaceStateDecodeError | ||
| 64 | | Error, | 62 | | Error, |
| 65 | never | 63 | never |
| 66 | > => | 64 | > => |
| @@ -73,15 +71,10 @@ export const resolveRuntimeLocalWorkspaceFromDeps = ( · 6 unmodified lines | |||
| 73 | }); | 71 | }); |
| 74 | } | 72 | } |
| 75 | 73 | ||
| 76 | const loadedConfig = yield* deps.workspaceConfigStore.load( | 74 | const loadedConfig = yield* deps.workspaceConfigStore.load(); |
| 77 | deps.runtimeLocalWorkspace.context, | 75 | const workspaceState = yield* deps.workspaceStateStore.load(); |
| 78 | ); | ||
| 79 | const workspaceState = yield* deps.workspaceStateStore.load( | ||
| 80 | deps.runtimeLocalWorkspace.context, | ||
| 81 | ); | ||
| 82 | 76 | ||
| 83 | return { | 77 | return { |
| 84 | context: deps.runtimeLocalWorkspace.context, | ||
| 85 | installation: deps.runtimeLocalWorkspace.installation, | 78 | installation: deps.runtimeLocalWorkspace.installation, |
| 86 | workspaceConfigStore: deps.workspaceConfigStore, | 79 | workspaceConfigStore: deps.workspaceConfigStore, |
| 87 | workspaceStateStore: deps.workspaceStateStore, | 80 | workspaceStateStore: deps.workspaceStateStore, |
| @@ -98,17 +91,16 @@ export const loadRuntimeSourceStoreDeps = ( · 10 unmodified lines | |||
| 98 | RuntimeSourceStoreDeps, | 91 | RuntimeSourceStoreDeps, |
| 99 | | RuntimeLocalWorkspaceUnavailableError | 92 | | RuntimeLocalWorkspaceUnavailableError |
| 100 | | RuntimeLocalWorkspaceMismatchError | 93 | | RuntimeLocalWorkspaceMismatchError |
| 101 | | LocalFileSystemError | ||
| 102 | | LocalExecutorConfigDecodeError | ||
| 103 | | LocalWorkspaceStateDecodeError | ||
| 104 | | Error, | 94 | | Error, |
| 105 | WorkspaceStorageServices | 95 | RuntimeSourceStoreServices |
| 106 | > => | 96 | > => |
| 107 | Effect.gen(function* () { | 97 | Effect.gen(function* () { |
| 108 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); | 98 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 109 | const workspaceConfigStore = yield* WorkspaceConfigStore; | 99 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 110 | const workspaceStateStore = yield* WorkspaceStateStore; | 100 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 111 | const sourceArtifactStore = yield* SourceArtifactStore; | 101 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 102 | const sourceTypeDeclarationsRefresher = | ||
| 103 | yield* SourceTypeDeclarationsRefresherService; | ||
| 112 | 104 | ||
| 113 | return { | 105 | return { |
| 114 | rows, | 106 | rows, |
| @@ -116,5 +108,6 @@ export const loadRuntimeSourceStoreDeps = ( · 1 unmodified line | |||
| 116 | workspaceConfigStore, | 108 | workspaceConfigStore, |
| 117 | workspaceStateStore, | 109 | workspaceStateStore, |
| 118 | sourceArtifactStore, | 110 | sourceArtifactStore, |
| 111 | sourceTypeDeclarationsRefresher, | ||
| 119 | }; | 112 | }; |
| 120 | }); | 113 | }); |
| @@ -6,7 +6,8 @@ import { · 5 unmodified lines | |||
| 6 | clearProviderGrantOrphanedAt, | 6 | clearProviderGrantOrphanedAt, |
| 7 | markProviderGrantOrphanedIfUnused, | 7 | markProviderGrantOrphanedIfUnused, |
| 8 | } from "../../auth/provider-grant-lifecycle"; | 8 | } from "../../auth/provider-grant-lifecycle"; |
| 9 | import type { LocalWorkspaceState } from "../../local/workspace-state"; | 9 | import type { DeleteSecretMaterial } from "../../workspace/secret-material-providers"; |
| 10 | import type { LocalWorkspaceState } from "../../workspace-state"; | ||
| 10 | import { | 11 | import { |
| 11 | stableSourceCatalogId, | 12 | stableSourceCatalogId, |
| 12 | stableSourceCatalogRevisionId, | 13 | stableSourceCatalogRevisionId, |
| @@ -39,6 +40,7 @@ export const removeSourceByIdWithDeps = ( · 26 unmodified lines | |||
| 39 | workspaceId: WorkspaceId; | 40 | workspaceId: WorkspaceId; |
| 40 | sourceId: Source["id"]; | 41 | sourceId: Source["id"]; |
| 41 | }, | 42 | }, |
| 43 | deleteSecretMaterial: DeleteSecretMaterial, | ||
| 42 | ): Effect.Effect<boolean, Error, never> => | 44 | ): Effect.Effect<boolean, Error, never> => |
| 43 | Effect.gen(function* () { | 45 | Effect.gen(function* () { |
| 44 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( | 46 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( |
| @@ -55,7 +57,6 @@ export const removeSourceByIdWithDeps = ( · 10 unmodified lines | |||
| 55 | }; | 57 | }; |
| 56 | delete sources[input.sourceId]; | 58 | delete sources[input.sourceId]; |
| 57 | yield* localWorkspace.workspaceConfigStore.writeProject({ | 59 | yield* localWorkspace.workspaceConfigStore.writeProject({ |
| 58 | context: localWorkspace.context, | ||
| 59 | config: { | 60 | config: { |
| 60 | ...projectConfig, | 61 | ...projectConfig, |
| 61 | sources, | 62 | sources, |
| @@ -69,11 +70,9 @@ export const removeSourceByIdWithDeps = ( · 7 unmodified lines | |||
| 69 | sources: remainingSources, | 70 | sources: remainingSources, |
| 70 | }; | 71 | }; |
| 71 | yield* localWorkspace.workspaceStateStore.write({ | 72 | yield* localWorkspace.workspaceStateStore.write({ |
| 72 | context: localWorkspace.context, | ||
| 73 | state: workspaceState, | 73 | state: workspaceState, |
| 74 | }); | 74 | }); |
| 75 | yield* localWorkspace.sourceArtifactStore.remove({ | 75 | yield* localWorkspace.sourceArtifactStore.remove({ |
| 76 | context: localWorkspace.context, | ||
| 77 | sourceId: input.sourceId, | 76 | sourceId: input.sourceId, |
| 78 | }); | 77 | }); |
| 79 | const existingAuthArtifacts = | 78 | const existingAuthArtifacts = |
| @@ -91,7 +90,7 @@ export const removeSourceByIdWithDeps = ( · 11 unmodified lines | |||
| 91 | workspaceId: input.workspaceId, | 90 | workspaceId: input.workspaceId, |
| 92 | sourceId: input.sourceId, | 91 | sourceId: input.sourceId, |
| 93 | }); | 92 | }); |
| 94 | yield* removeAuthArtifactsForSource(deps.rows, input); | 93 | yield* removeAuthArtifactsForSource(deps.rows, input, deleteSecretMaterial); |
| 95 | yield* Effect.forEach( | 94 | yield* Effect.forEach( |
| 96 | [...removedGrantIds], | 95 | [...removedGrantIds], |
| 97 | (grantId) => | 96 | (grantId) => |
| @@ -112,6 +111,7 @@ export const persistSourceWithDeps = ( · 14 unmodified lines | |||
| 112 | options: { | 111 | options: { |
| 113 | actorAccountId?: AccountId | null; | 112 | actorAccountId?: AccountId | null; |
| 114 | } = {}, | 113 | } = {}, |
| 114 | deleteSecretMaterial: DeleteSecretMaterial, | ||
| 115 | ): Effect.Effect<Source, Error, never> => | 115 | ): Effect.Effect<Source, Error, never> => |
| 116 | Effect.gen(function* () { | 116 | Effect.gen(function* () { |
| 117 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( | 117 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( |
| @@ -155,7 +155,6 @@ export const persistSourceWithDeps = ( · 37 unmodified lines | |||
| 155 | config: localWorkspace.loadedConfig.config, | 155 | config: localWorkspace.loadedConfig.config, |
| 156 | }); | 156 | }); |
| 157 | yield* localWorkspace.workspaceConfigStore.writeProject({ | 157 | yield* localWorkspace.workspaceConfigStore.writeProject({ |
| 158 | context: localWorkspace.context, | ||
| 159 | config: { | 158 | config: { |
| 160 | ...projectConfig, | 159 | ...projectConfig, |
| 161 | sources, | 160 | sources, |
| @@ -175,7 +174,7 @@ export const persistSourceWithDeps = ( · 13 unmodified lines | |||
| 175 | if (existingRuntimeAuthArtifact !== null) { | 174 | if (existingRuntimeAuthArtifact !== null) { |
| 176 | yield* removeAuthLeaseAndSecrets(deps.rows, { | 175 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 177 | authArtifactId: existingRuntimeAuthArtifact.id, | 176 | authArtifactId: existingRuntimeAuthArtifact.id, |
| 178 | }); | 177 | }, deleteSecretMaterial); |
| 179 | } | 178 | } |
| 180 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ | 179 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 181 | workspaceId: nextSource.workspaceId, | 180 | workspaceId: nextSource.workspaceId, |
| @@ -191,20 +190,20 @@ export const persistSourceWithDeps = ( · 9 unmodified lines | |||
| 191 | ) { | 190 | ) { |
| 192 | yield* removeAuthLeaseAndSecrets(deps.rows, { | 191 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 193 | authArtifactId: existingRuntimeAuthArtifact.id, | 192 | authArtifactId: existingRuntimeAuthArtifact.id, |
| 194 | }); | 193 | }, deleteSecretMaterial); |
| 195 | } | 194 | } |
| 196 | } | 195 | } |
| 197 | 196 | ||
| 198 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { | 197 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { |
| 199 | previous: existingRuntimeAuthArtifact ?? null, | 198 | previous: existingRuntimeAuthArtifact ?? null, |
| 200 | next: runtimeAuthArtifact, | 199 | next: runtimeAuthArtifact, |
| 201 | }); | 200 | }, deleteSecretMaterial); |
| 202 | 201 | ||
| 203 | if (importAuthArtifact === null) { | 202 | if (importAuthArtifact === null) { |
| 204 | if (existingImportAuthArtifact !== null) { | 203 | if (existingImportAuthArtifact !== null) { |
| 205 | yield* removeAuthLeaseAndSecrets(deps.rows, { | 204 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 206 | authArtifactId: existingImportAuthArtifact.id, | 205 | authArtifactId: existingImportAuthArtifact.id, |
| 207 | }); | 206 | }, deleteSecretMaterial); |
| 208 | } | 207 | } |
| 209 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ | 208 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 210 | workspaceId: nextSource.workspaceId, | 209 | workspaceId: nextSource.workspaceId, |
| @@ -220,14 +219,14 @@ export const persistSourceWithDeps = ( · 9 unmodified lines | |||
| 220 | ) { | 219 | ) { |
| 221 | yield* removeAuthLeaseAndSecrets(deps.rows, { | 220 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 222 | authArtifactId: existingImportAuthArtifact.id, | 221 | authArtifactId: existingImportAuthArtifact.id, |
| 223 | }); | 222 | }, deleteSecretMaterial); |
| 224 | } | 223 | } |
| 225 | } | 224 | } |
| 226 | 225 | ||
| 227 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { | 226 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { |
| 228 | previous: existingImportAuthArtifact ?? null, | 227 | previous: existingImportAuthArtifact ?? null, |
| 229 | next: importAuthArtifact, | 228 | next: importAuthArtifact, |
| 230 | }); | 229 | }, deleteSecretMaterial); |
| 231 | 230 | ||
| 232 | const previousGrantIds = providerGrantIdsFromArtifacts([ | 231 | const previousGrantIds = providerGrantIdsFromArtifacts([ |
| 233 | existingRuntimeAuthArtifact, | 232 | existingRuntimeAuthArtifact, |
| @@ -271,7 +270,6 @@ export const persistSourceWithDeps = ( · 37 unmodified lines | |||
| 271 | }, | 270 | }, |
| 272 | }; | 271 | }; |
| 273 | yield* localWorkspace.workspaceStateStore.write({ | 272 | yield* localWorkspace.workspaceStateStore.write({ |
| 274 | context: localWorkspace.context, | ||
| 275 | state: workspaceState, | 273 | state: workspaceState, |
| 276 | }); | 274 | }); |
| 277 | 275 | ||
| @@ -10,17 +10,13 @@ import * as Effect from "effect/Effect"; · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | import { sourceAuthFromAuthArtifact } from "../../auth/auth-artifacts"; | 11 | import { sourceAuthFromAuthArtifact } from "../../auth/auth-artifacts"; |
| 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; | 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; |
| 13 | import { refreshWorkspaceSourceTypeDeclarationsInBackground } from "../../catalog/source/type-declarations"; | 13 | import type { LoadedLocalExecutorConfig } from "../../workspace-config"; |
| 14 | import type { LoadedLocalExecutorConfig } from "../../local/config"; | ||
| 15 | import { | 14 | import { |
| 16 | LocalConfiguredSourceNotFoundError, | 15 | LocalConfiguredSourceNotFoundError, |
| 17 | LocalExecutorConfigDecodeError, | ||
| 18 | LocalFileSystemError, | ||
| 19 | LocalWorkspaceStateDecodeError, | ||
| 20 | RuntimeLocalWorkspaceMismatchError, | 16 | RuntimeLocalWorkspaceMismatchError, |
| 21 | RuntimeLocalWorkspaceUnavailableError, | 17 | RuntimeLocalWorkspaceUnavailableError, |
| 22 | } from "../../local/errors"; | 18 | } from "../../workspace-errors"; |
| 23 | import type { LocalWorkspaceState } from "../../local/workspace-state"; | 19 | import type { LocalWorkspaceState } from "../../workspace-state"; |
| 24 | import { getSourceAdapter } from "../source-adapters"; | 20 | import { getSourceAdapter } from "../source-adapters"; |
| 25 | import { | 21 | import { |
| 26 | resolveRuntimeLocalWorkspaceFromDeps, | 22 | resolveRuntimeLocalWorkspaceFromDeps, |
| @@ -131,9 +127,6 @@ export const loadSourcesInWorkspaceWithDeps = ( · 104 unmodified lines | |||
| 131 | readonly Source[], | 127 | readonly Source[], |
| 132 | | RuntimeLocalWorkspaceUnavailableError | 128 | | RuntimeLocalWorkspaceUnavailableError |
| 133 | | RuntimeLocalWorkspaceMismatchError | 129 | | RuntimeLocalWorkspaceMismatchError |
| 134 | | LocalFileSystemError | ||
| 135 | | LocalExecutorConfigDecodeError | ||
| 136 | | LocalWorkspaceStateDecodeError | ||
| 137 | | LocalConfiguredSourceNotFoundError | 130 | | LocalConfiguredSourceNotFoundError |
| 138 | | Error, | 131 | | Error, |
| 139 | never | 132 | never |
| @@ -191,7 +184,6 @@ export const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 51 unmodified lines | |||
| 191 | const entries = yield* Effect.forEach(sources, (source) => | 184 | const entries = yield* Effect.forEach(sources, (source) => |
| 192 | Effect.map( | 185 | Effect.map( |
| 193 | deps.sourceArtifactStore.read({ | 186 | deps.sourceArtifactStore.read({ |
| 194 | context: localWorkspace.context, | ||
| 195 | sourceId: source.id, | 187 | sourceId: source.id, |
| 196 | }), | 188 | }), |
| 197 | (artifact) => | 189 | (artifact) => |
| @@ -204,13 +196,10 @@ export const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 6 unmodified lines | |||
| 204 | ), | 196 | ), |
| 205 | ); | 197 | ); |
| 206 | 198 | ||
| 207 | yield* Effect.sync(() => { | 199 | yield* deps.sourceTypeDeclarationsRefresher.refreshWorkspaceInBackground({ |
| 208 | refreshWorkspaceSourceTypeDeclarationsInBackground({ | 200 | entries: entries.filter( |
| 209 | context: localWorkspace.context, | 201 | (entry): entry is NonNullable<typeof entry> => entry !== null, |
| 210 | entries: entries.filter( | 202 | ), |
| 211 | (entry): entry is NonNullable<typeof entry> => entry !== null, | ||
| 212 | ), | ||
| 213 | }); | ||
| 214 | }); | 203 | }); |
| 215 | }).pipe( | 204 | }).pipe( |
| 216 | Effect.withSpan("source.types.refresh_workspace.schedule", { | 205 | Effect.withSpan("source.types.refresh_workspace.schedule", { |
| @@ -236,9 +225,6 @@ export const listLinkedSecretSourcesInWorkspaceWithDeps = ( · 19 unmodified lines | |||
| 236 | Map<string, Array<{ sourceId: string; sourceName: string }>>, | 225 | Map<string, Array<{ sourceId: string; sourceName: string }>>, |
| 237 | | RuntimeLocalWorkspaceUnavailableError | 226 | | RuntimeLocalWorkspaceUnavailableError |
| 238 | | RuntimeLocalWorkspaceMismatchError | 227 | | RuntimeLocalWorkspaceMismatchError |
| 239 | | LocalFileSystemError | ||
| 240 | | LocalExecutorConfigDecodeError | ||
| 241 | | LocalWorkspaceStateDecodeError | ||
| 242 | | LocalConfiguredSourceNotFoundError | 228 | | LocalConfiguredSourceNotFoundError |
| 243 | | Error, | 229 | | Error, |
| 244 | never | 230 | never |
| @@ -295,9 +281,6 @@ export const loadSourceByIdWithDeps = ( · 50 unmodified lines | |||
| 295 | Source, | 281 | Source, |
| 296 | | RuntimeLocalWorkspaceUnavailableError | 282 | | RuntimeLocalWorkspaceUnavailableError |
| 297 | | RuntimeLocalWorkspaceMismatchError | 283 | | RuntimeLocalWorkspaceMismatchError |
| 298 | | LocalFileSystemError | ||
| 299 | | LocalExecutorConfigDecodeError | ||
| 300 | | LocalWorkspaceStateDecodeError | ||
| 301 | | LocalConfiguredSourceNotFoundError | 284 | | LocalConfiguredSourceNotFoundError |
| 302 | | Error, | 285 | | Error, |
| 303 | never | 286 | never |
| @@ -0,0 +1,9 @@ | |||
| 1 | import type { LocalExecutorConfig } from "#schema"; | ||
| 2 | |||
| 3 | export type LoadedExecutorConfig = { | ||
| 4 | config: LocalExecutorConfig | null; | ||
| 5 | homeConfig: LocalExecutorConfig | null; | ||
| 6 | projectConfig: LocalExecutorConfig | null; | ||
| 7 | }; | ||
| 8 | |||
| 9 | export type LoadedLocalExecutorConfig = LoadedExecutorConfig; | ||
| @@ -0,0 +1,36 @@ | |||
| 1 | import * as Data from "effect/Data"; | ||
| 2 | |||
| 3 | export class RuntimeLocalWorkspaceUnavailableError extends Data.TaggedError( | ||
| 4 | "RuntimeLocalWorkspaceUnavailableError", | ||
| 5 | )<{ | ||
| 6 | readonly message: string; | ||
| 7 | }> {} | ||
| 8 | |||
| 9 | export class RuntimeLocalWorkspaceMismatchError extends Data.TaggedError( | ||
| 10 | "RuntimeLocalWorkspaceMismatchError", | ||
| 11 | )<{ | ||
| 12 | readonly message: string; | ||
| 13 | readonly requestedWorkspaceId: string; | ||
| 14 | readonly activeWorkspaceId: string; | ||
| 15 | }> {} | ||
| 16 | |||
| 17 | export class LocalConfiguredSourceNotFoundError extends Data.TaggedError( | ||
| 18 | "LocalConfiguredSourceNotFoundError", | ||
| 19 | )<{ | ||
| 20 | readonly message: string; | ||
| 21 | readonly sourceId: string; | ||
| 22 | }> {} | ||
| 23 | |||
| 24 | export class LocalSourceArtifactMissingError extends Data.TaggedError( | ||
| 25 | "LocalSourceArtifactMissingError", | ||
| 26 | )<{ | ||
| 27 | readonly message: string; | ||
| 28 | readonly sourceId: string; | ||
| 29 | }> {} | ||
| 30 | |||
| 31 | export class LocalUnsupportedSourceKindError extends Data.TaggedError( | ||
| 32 | "LocalUnsupportedSourceKindError", | ||
| 33 | )<{ | ||
| 34 | readonly message: string; | ||
| 35 | readonly kind: string; | ||
| 36 | }> {} | ||
| @@ -0,0 +1,42 @@ | |||
| 1 | import { | ||
| 2 | PolicyIdSchema, | ||
| 3 | SourceStatusSchema, | ||
| 4 | TimestampMsSchema, | ||
| 5 | } from "#schema"; | ||
| 6 | import * as Schema from "effect/Schema"; | ||
| 7 | |||
| 8 | const LocalWorkspaceSourceStateSchema = Schema.Struct({ | ||
| 9 | status: SourceStatusSchema, | ||
| 10 | lastError: Schema.NullOr(Schema.String), | ||
| 11 | sourceHash: Schema.NullOr(Schema.String), | ||
| 12 | createdAt: TimestampMsSchema, | ||
| 13 | updatedAt: TimestampMsSchema, | ||
| 14 | }); | ||
| 15 | |||
| 16 | const LocalWorkspacePolicyStateSchema = Schema.Struct({ | ||
| 17 | id: PolicyIdSchema, | ||
| 18 | createdAt: TimestampMsSchema, | ||
| 19 | updatedAt: TimestampMsSchema, | ||
| 20 | }); | ||
| 21 | |||
| 22 | export const LocalWorkspaceStateSchema = Schema.Struct({ | ||
| 23 | version: Schema.Literal(1), | ||
| 24 | sources: Schema.Record({ | ||
| 25 | key: Schema.String, | ||
| 26 | value: LocalWorkspaceSourceStateSchema, | ||
| 27 | }), | ||
| 28 | policies: Schema.Record({ | ||
| 29 | key: Schema.String, | ||
| 30 | value: LocalWorkspacePolicyStateSchema, | ||
| 31 | }), | ||
| 32 | }); | ||
| 33 | |||
| 34 | export type LocalWorkspaceSourceState = typeof LocalWorkspaceSourceStateSchema.Type; | ||
| 35 | export type LocalWorkspacePolicyState = typeof LocalWorkspacePolicyStateSchema.Type; | ||
| 36 | export type LocalWorkspaceState = typeof LocalWorkspaceStateSchema.Type; | ||
| 37 | |||
| 38 | export const defaultLocalWorkspaceState = (): LocalWorkspaceState => ({ | ||
| 39 | version: 1, | ||
| 40 | sources: {}, | ||
| 41 | policies: {}, | ||
| 42 | }); | ||
File renamed without changes.
| @@ -4,17 +4,15 @@ import * as Layer from "effect/Layer"; · 3 unmodified lines | |||
| 4 | import * as Option from "effect/Option"; | 4 | import * as Option from "effect/Option"; |
| 5 | 5 | ||
| 6 | import type { AccountId, WorkspaceId } from "#schema"; | 6 | import type { AccountId, WorkspaceId } from "#schema"; |
| 7 | import type { | 7 | import type { ExecutorWorkspaceContext } from "../../workspace"; |
| 8 | LoadedLocalExecutorConfig, | 8 | import type { LoadedLocalExecutorConfig } from "../workspace-config"; |
| 9 | ResolvedLocalWorkspaceContext, | ||
| 10 | } from "./config"; | ||
| 11 | import { | 9 | import { |
| 12 | RuntimeLocalWorkspaceMismatchError, | 10 | RuntimeLocalWorkspaceMismatchError, |
| 13 | RuntimeLocalWorkspaceUnavailableError, | 11 | RuntimeLocalWorkspaceUnavailableError, |
| 14 | } from "./errors"; | 12 | } from "../workspace-errors"; |
| 15 | 13 | ||
| 16 | export type RuntimeLocalWorkspaceState = { | 14 | export type RuntimeLocalWorkspaceState = { |
| 17 | context: ResolvedLocalWorkspaceContext; | 15 | workspace: ExecutorWorkspaceContext; |
| 18 | installation: { | 16 | installation: { |
| 19 | workspaceId: WorkspaceId; | 17 | workspaceId: WorkspaceId; |
| 20 | accountId: AccountId; | 18 | accountId: AccountId; |
| @@ -0,0 +1,63 @@ | |||
| 1 | import type { InstanceConfig } from "../../local/contracts"; | ||
| 2 | import * as Context from "effect/Context"; | ||
| 3 | import * as Effect from "effect/Effect"; | ||
| 4 | |||
| 5 | import type { | ||
| 6 | SecretMaterialPurpose, | ||
| 7 | SecretRef, | ||
| 8 | } from "#schema"; | ||
| 9 | |||
| 10 | export type SecretMaterialResolveContext = { | ||
| 11 | params?: Readonly<Record<string, string | undefined>>; | ||
| 12 | }; | ||
| 13 | |||
| 14 | export type ResolveSecretMaterial = (input: { | ||
| 15 | ref: SecretRef; | ||
| 16 | context?: SecretMaterialResolveContext; | ||
| 17 | }) => Effect.Effect<string, Error, never>; | ||
| 18 | |||
| 19 | export type StoreSecretMaterial = (input: { | ||
| 20 | purpose: SecretMaterialPurpose; | ||
| 21 | value: string; | ||
| 22 | name?: string | null; | ||
| 23 | providerId?: string; | ||
| 24 | }) => Effect.Effect<SecretRef, Error, never>; | ||
| 25 | |||
| 26 | export type DeleteSecretMaterial = ( | ||
| 27 | ref: SecretRef, | ||
| 28 | ) => Effect.Effect<boolean, Error, never>; | ||
| 29 | |||
| 30 | export type UpdateSecretMaterial = (input: { | ||
| 31 | ref: SecretRef; | ||
| 32 | name?: string | null; | ||
| 33 | value?: string; | ||
| 34 | }) => Effect.Effect<{ | ||
| 35 | id: string; | ||
| 36 | providerId: string; | ||
| 37 | name: string | null; | ||
| 38 | purpose: string; | ||
| 39 | createdAt: number; | ||
| 40 | updatedAt: number; | ||
| 41 | }, Error, never>; | ||
| 42 | |||
| 43 | export type ResolveInstanceConfig = () => Effect.Effect<InstanceConfig, Error, never>; | ||
| 44 | |||
| 45 | export class SecretMaterialResolverService extends Context.Tag( | ||
| 46 | "#runtime/SecretMaterialResolverService", | ||
| 47 | )<SecretMaterialResolverService, ResolveSecretMaterial>() {} | ||
| 48 | |||
| 49 | export class SecretMaterialStorerService extends Context.Tag( | ||
| 50 | "#runtime/SecretMaterialStorerService", | ||
| 51 | )<SecretMaterialStorerService, StoreSecretMaterial>() {} | ||
| 52 | |||
| 53 | export class SecretMaterialDeleterService extends Context.Tag( | ||
| 54 | "#runtime/SecretMaterialDeleterService", | ||
| 55 | )<SecretMaterialDeleterService, DeleteSecretMaterial>() {} | ||
| 56 | |||
| 57 | export class SecretMaterialUpdaterService extends Context.Tag( | ||
| 58 | "#runtime/SecretMaterialUpdaterService", | ||
| 59 | )<SecretMaterialUpdaterService, UpdateSecretMaterial>() {} | ||
| 60 | |||
| 61 | export class LocalInstanceConfigService extends Context.Tag( | ||
| 62 | "#runtime/LocalInstanceConfigService", | ||
| 63 | )<LocalInstanceConfigService, ResolveInstanceConfig>() {} | ||
| @@ -0,0 +1,95 @@ | |||
| 1 | import type { LocalInstallation, LocalExecutorConfig } from "#schema"; | ||
| 2 | import * as Context from "effect/Context"; | ||
| 3 | import * as Layer from "effect/Layer"; | ||
| 4 | |||
| 5 | import type { LoadedLocalExecutorConfig } from "../workspace-config"; | ||
| 6 | import type { LocalSourceArtifact } from "../source-artifacts"; | ||
| 7 | import type { LocalWorkspaceState } from "../workspace-state"; | ||
| 8 | import type { SourceCatalogSyncResult } from "@executor/source-core"; | ||
| 9 | import type { Source } from "#schema"; | ||
| 10 | |||
| 11 | export type InstallationStoreShape = { | ||
| 12 | load: () => import("effect/Effect").Effect<LocalInstallation, Error, never>; | ||
| 13 | getOrProvision: () => import("effect/Effect").Effect<LocalInstallation, Error, never>; | ||
| 14 | }; | ||
| 15 | |||
| 16 | export class InstallationStore extends Context.Tag( | ||
| 17 | "#runtime/InstallationStore", | ||
| 18 | )<InstallationStore, InstallationStoreShape>() {} | ||
| 19 | |||
| 20 | export type WorkspaceConfigStoreShape = { | ||
| 21 | load: () => import("effect/Effect").Effect<LoadedLocalExecutorConfig, Error, never>; | ||
| 22 | writeProject: (input: { | ||
| 23 | config: LocalExecutorConfig; | ||
| 24 | }) => import("effect/Effect").Effect<void, Error, never>; | ||
| 25 | resolveRelativePath: (input: { path: string; workspaceRoot: string }) => string; | ||
| 26 | }; | ||
| 27 | |||
| 28 | export class WorkspaceConfigStore extends Context.Tag( | ||
| 29 | "#runtime/WorkspaceConfigStore", | ||
| 30 | )<WorkspaceConfigStore, WorkspaceConfigStoreShape>() {} | ||
| 31 | |||
| 32 | export type WorkspaceStateStoreShape = { | ||
| 33 | load: () => import("effect/Effect").Effect<LocalWorkspaceState, Error, never>; | ||
| 34 | write: (input: { | ||
| 35 | state: LocalWorkspaceState; | ||
| 36 | }) => import("effect/Effect").Effect<void, Error, never>; | ||
| 37 | }; | ||
| 38 | |||
| 39 | export class WorkspaceStateStore extends Context.Tag( | ||
| 40 | "#runtime/WorkspaceStateStore", | ||
| 41 | )<WorkspaceStateStore, WorkspaceStateStoreShape>() {} | ||
| 42 | |||
| 43 | export type SourceArtifactStoreShape = { | ||
| 44 | build: (input: { | ||
| 45 | source: Source; | ||
| 46 | syncResult: SourceCatalogSyncResult; | ||
| 47 | }) => LocalSourceArtifact; | ||
| 48 | read: (input: { | ||
| 49 | sourceId: string; | ||
| 50 | }) => import("effect/Effect").Effect<LocalSourceArtifact | null, Error, never>; | ||
| 51 | write: (input: { | ||
| 52 | sourceId: string; | ||
| 53 | artifact: LocalSourceArtifact; | ||
| 54 | }) => import("effect/Effect").Effect<void, Error, never>; | ||
| 55 | remove: (input: { | ||
| 56 | sourceId: string; | ||
| 57 | }) => import("effect/Effect").Effect<void, Error, never>; | ||
| 58 | }; | ||
| 59 | |||
| 60 | export class SourceArtifactStore extends Context.Tag( | ||
| 61 | "#runtime/SourceArtifactStore", | ||
| 62 | )<SourceArtifactStore, SourceArtifactStoreShape>() {} | ||
| 63 | |||
| 64 | export type LocalStorageServices = | ||
| 65 | | InstallationStore | ||
| 66 | | WorkspaceConfigStore | ||
| 67 | | WorkspaceStateStore | ||
| 68 | | SourceArtifactStore; | ||
| 69 | |||
| 70 | export type WorkspaceStorageServices = | ||
| 71 | | WorkspaceConfigStore | ||
| 72 | | WorkspaceStateStore | ||
| 73 | | SourceArtifactStore; | ||
| 74 | |||
| 75 | export const makeWorkspaceStorageLayer = (input: { | ||
| 76 | workspaceConfigStore: WorkspaceConfigStoreShape; | ||
| 77 | workspaceStateStore: WorkspaceStateStoreShape; | ||
| 78 | sourceArtifactStore: SourceArtifactStoreShape; | ||
| 79 | }) => | ||
| 80 | Layer.mergeAll( | ||
| 81 | Layer.succeed(WorkspaceConfigStore, input.workspaceConfigStore), | ||
| 82 | Layer.succeed(WorkspaceStateStore, input.workspaceStateStore), | ||
| 83 | Layer.succeed(SourceArtifactStore, input.sourceArtifactStore), | ||
| 84 | ); | ||
| 85 | |||
| 86 | export const makeLocalStorageLayer = (input: { | ||
| 87 | installationStore: InstallationStoreShape; | ||
| 88 | workspaceConfigStore: WorkspaceConfigStoreShape; | ||
| 89 | workspaceStateStore: WorkspaceStateStoreShape; | ||
| 90 | sourceArtifactStore: SourceArtifactStoreShape; | ||
| 91 | }) => | ||
| 92 | Layer.mergeAll( | ||
| 93 | Layer.succeed(InstallationStore, input.installationStore), | ||
| 94 | makeWorkspaceStorageLayer(input), | ||
| 95 | ); | ||
| @@ -1,15 +1,8 @@ | |||
| 1 | import { FileSystem } from "@effect/platform"; | ||
| 2 | import * as Effect from "effect/Effect"; | 1 | import * as Effect from "effect/Effect"; |
| 3 | 2 | ||
| 4 | import { | 3 | import { type LoadedLocalExecutorConfig } from "../workspace-config"; |
| 5 | type LoadedLocalExecutorConfig, | 4 | import { WorkspaceStateStore } from "./storage"; |
| 6 | type ResolvedLocalWorkspaceContext, | 5 | import { type LocalWorkspaceState } from "../workspace-state"; |
| 7 | } from "./config"; | ||
| 8 | import { | ||
| 9 | loadLocalWorkspaceState, | ||
| 10 | writeLocalWorkspaceState, | ||
| 11 | type LocalWorkspaceState, | ||
| 12 | } from "./workspace-state"; | ||
| 13 | 6 | ||
| 14 | const trimOrNull = (value: string | null | undefined): string | null => { | 7 | const trimOrNull = (value: string | null | undefined): string | null => { |
| 15 | if (value == null) { | 8 | if (value == null) { |
| @@ -41,11 +34,11 @@ export const derivePolicyConfigKey = ( · 25 unmodified lines | |||
| 41 | }; | 34 | }; |
| 42 | 35 | ||
| 43 | const pruneLocalWorkspaceState = (input: { | 36 | const pruneLocalWorkspaceState = (input: { |
| 44 | context: ResolvedLocalWorkspaceContext; | ||
| 45 | loadedConfig: LoadedLocalExecutorConfig; | 37 | loadedConfig: LoadedLocalExecutorConfig; |
| 46 | }): Effect.Effect<LocalWorkspaceState, Error, FileSystem.FileSystem> => | 38 | }): Effect.Effect<LocalWorkspaceState, Error, WorkspaceStateStore> => |
| 47 | Effect.gen(function* () { | 39 | Effect.gen(function* () { |
| 48 | const currentState = yield* loadLocalWorkspaceState(input.context); | 40 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 41 | const currentState = yield* workspaceStateStore.load(); | ||
| 49 | 42 | ||
| 50 | const configuredSourceIds = new Set( | 43 | const configuredSourceIds = new Set( |
| 51 | Object.keys(input.loadedConfig.config?.sources ?? {}), | 44 | Object.keys(input.loadedConfig.config?.sources ?? {}), |
| @@ -72,8 +65,7 @@ const pruneLocalWorkspaceState = (input: { · 20 unmodified lines | |||
| 72 | return currentState; | 65 | return currentState; |
| 73 | } | 66 | } |
| 74 | 67 | ||
| 75 | yield* writeLocalWorkspaceState({ | 68 | yield* workspaceStateStore.write({ |
| 76 | context: input.context, | ||
| 77 | state: nextState, | 69 | state: nextState, |
| 78 | }); | 70 | }); |
| 79 | 71 | ||
| @@ -81,12 +73,10 @@ const pruneLocalWorkspaceState = (input: { · 1 unmodified line | |||
| 81 | }); | 73 | }); |
| 82 | 74 | ||
| 83 | export const synchronizeLocalWorkspaceState = (input: { | 75 | export const synchronizeLocalWorkspaceState = (input: { |
| 84 | context: ResolvedLocalWorkspaceContext; | ||
| 85 | loadedConfig: LoadedLocalExecutorConfig; | 76 | loadedConfig: LoadedLocalExecutorConfig; |
| 86 | }): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, FileSystem.FileSystem> => | 77 | }): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, WorkspaceStateStore> => |
| 87 | Effect.gen(function* () { | 78 | Effect.gen(function* () { |
| 88 | yield* pruneLocalWorkspaceState({ | 79 | yield* pruneLocalWorkspaceState({ |
| 89 | context: input.context, | ||
| 90 | loadedConfig: input.loadedConfig, | 80 | loadedConfig: input.loadedConfig, |
| 91 | }); | 81 | }); |
| 92 | 82 | ||
| @@ -16,7 +16,7 @@ import { · 15 unmodified lines | |||
| 16 | } from "../errors"; | 16 | } from "../errors"; |
| 17 | import * as Effect from "effect/Effect"; | 17 | import * as Effect from "effect/Effect"; |
| 18 | 18 | ||
| 19 | import { LocalSourceArtifactMissingError } from "../runtime/local/errors"; | 19 | import { LocalSourceArtifactMissingError } from "../runtime/workspace-errors"; |
| 20 | import { operationErrors } from "../runtime/policy/operation-errors"; | 20 | import { operationErrors } from "../runtime/policy/operation-errors"; |
| 21 | import { | 21 | import { |
| 22 | buildLoadedSourceCatalogToolContract, | 22 | buildLoadedSourceCatalogToolContract, |
| @@ -0,0 +1,15 @@ | |||
| 1 | import type { | ||
| 2 | AccountId, | ||
| 3 | WorkspaceId, | ||
| 4 | } from "./schema"; | ||
| 5 | |||
| 6 | export type ExecutorWorkspaceDescriptor = { | ||
| 7 | workspaceName: string; | ||
| 8 | workspaceRoot?: string | null; | ||
| 9 | metadata?: Readonly<Record<string, unknown>>; | ||
| 10 | }; | ||
| 11 | |||
| 12 | export type ExecutorWorkspaceContext = ExecutorWorkspaceDescriptor & { | ||
| 13 | workspaceId: WorkspaceId; | ||
| 14 | accountId: AccountId; | ||
| 15 | }; | ||