Unified · Split
| @@ -22,14 +22,16 @@ import { · 21 unmodified lines | ||
| 22 | 22 | controlPlaneOpenApiSpec, |
| 23 | 23 | } from "@executor/platform-api"; |
| 24 | 24 | import { |
| 25 | − buildLocalSourceArtifact, | |
| 26 | 25 | catalogSyncResultFromMcpManifest, |
| 27 | − deriveLocalInstallation, | |
| 26 | + type ResolveExecutionEnvironment, | |
| 27 | +} from "@executor/platform-sdk/runtime"; | |
| 28 | +import { | |
| 29 | + buildLocalSourceArtifact, | |
| 30 | + getOrProvisionLocalInstallation, | |
| 28 | 31 | resolveLocalWorkspaceContext, |
| 29 | 32 | writeLocalSourceArtifact, |
| 30 | 33 | writeProjectLocalExecutorConfig, |
| 31 | − type ResolveExecutionEnvironment, | |
| 32 | −} from "@executor/platform-sdk/runtime"; | |
| 34 | +} from "@executor/platform-sdk-file"; | |
| 33 | 35 | import { SourceIdSchema } from "@executor/platform-sdk/schema"; |
| 34 | 36 | import { makeSesExecutor } from "@executor/runtime-ses"; |
| 35 | 37 | import { Client } from "@modelcontextprotocol/sdk/client/index.js"; |
| @@ -97,7 +99,7 @@ const writeConfiguredLocalMcpSource = (input: { · 61 unmodified lines | ||
| 97 | 99 | const context = yield* resolveLocalWorkspaceContext({ |
| 98 | 100 | workspaceRoot: input.workspaceRoot, |
| 99 | 101 | }); |
| 100 | − const installation = deriveLocalInstallation(context); | |
| 102 | + const installation = yield* getOrProvisionLocalInstallation({ context }); | |
| 101 | 103 | |
| 102 | 104 | yield* writeProjectLocalExecutorConfig({ |
| 103 | 105 | context, |
| @@ -30,6 +30,7 @@ · 29 unmodified lines | ||
| 30 | 30 | "@executor/platform-api": "workspace:*", |
| 31 | 31 | "@executor/platform-internal": "workspace:*", |
| 32 | 32 | "@executor/platform-sdk": "workspace:*", |
| 33 | + "@executor/platform-sdk-file": "workspace:*", | |
| 33 | 34 | "@executor/server": "workspace:*", |
| 34 | 35 | "effect": "catalog:", |
| 35 | 36 | }, |
| @@ -136,6 +137,7 @@ · 100 unmodified lines | ||
| 136 | 137 | "@effect/platform": "catalog:", |
| 137 | 138 | "@effect/platform-node": "catalog:", |
| 138 | 139 | "@effect/vitest": "catalog:", |
| 140 | + "@executor/platform-sdk-file": "workspace:*", | |
| 139 | 141 | "@types/jsdom": "^21.1.7", |
| 140 | 142 | "@types/node": "catalog:", |
| 141 | 143 | "@types/react": "^19.2.10", |
| @@ -332,6 +334,29 @@ · 190 unmodified lines | ||
| 332 | 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 | 360 | "packages/platform/server": { |
| 336 | 361 | "name": "@executor/server", |
| 337 | 362 | "version": "0.0.0", |
| @@ -343,6 +368,7 @@ · 5 unmodified lines | ||
| 343 | 368 | "@executor/platform-api": "workspace:*", |
| 344 | 369 | "@executor/platform-internal": "workspace:*", |
| 345 | 370 | "@executor/platform-sdk": "workspace:*", |
| 371 | + "@executor/platform-sdk-file": "workspace:*", | |
| 346 | 372 | "@opentelemetry/api": "^1.9.0", |
| 347 | 373 | "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0", |
| 348 | 374 | "@opentelemetry/resources": "^2.6.0", |
| @@ -684,6 +710,8 @@ · 335 unmodified lines | ||
| 684 | 710 | |
| 685 | 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 | 715 | "@executor/react": ["@executor/react@workspace:packages/clients/react"], |
| 688 | 716 | |
| 689 | 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 | 21 | import { |
| 22 | 22 | ControlPlaneStore, |
| 23 | + LocalInstanceConfigService, | |
| 24 | + SecretMaterialDeleterService, | |
| 25 | + SecretMaterialStorerService, | |
| 26 | + SecretMaterialUpdaterService, | |
| 23 | 27 | type ControlPlaneStoreShape, |
| 24 | 28 | type WorkspaceInternalToolContext, |
| 25 | − RuntimeLocalWorkspaceState, | |
| 26 | 29 | RuntimeSourceAuthService, |
| 27 | 30 | RuntimeSourceCatalogSyncService, |
| 28 | 31 | RuntimeSourceStore, |
| 29 | 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 | 39 | SourceArtifactStore, |
| 31 | 40 | type SourceArtifactStoreShape, |
| 32 | 41 | WorkspaceConfigStore, |
| @@ -34,8 +43,7 @@ import { · 1 unmodified line | ||
| 34 | 43 | WorkspaceStateStore, |
| 35 | 44 | type WorkspaceStateStoreShape, |
| 36 | 45 | makeWorkspaceStorageLayer, |
| 37 | − provideOptionalRuntimeLocalWorkspace, | |
| 38 | −} from "@executor/platform-sdk/runtime"; | |
| 46 | +} from "../../../sdk/src/runtime/workspace/storage"; | |
| 39 | 47 | import { |
| 40 | 48 | CreateSecretPayloadSchema, |
| 41 | 49 | CreateSecretResultSchema, |
| @@ -53,7 +61,6 @@ import { · 11 unmodified lines | ||
| 53 | 61 | deleteLocalSecret, |
| 54 | 62 | discoverSource, |
| 55 | 63 | discoverSourceInspectionTools, |
| 56 | − getLocalInstanceConfig, | |
| 57 | 64 | getPolicy, |
| 58 | 65 | getSource, |
| 59 | 66 | getSourceInspection, |
| @@ -201,6 +208,10 @@ const makeRuntimeLayer = (input: { · 141 unmodified lines | ||
| 201 | 208 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 202 | 209 | workspaceStateStore: WorkspaceStateStoreShape; |
| 203 | 210 | sourceArtifactStore: SourceArtifactStoreShape; |
| 211 | + instanceConfigResolver: WorkspaceInternalToolContext["instanceConfigResolver"]; | |
| 212 | + storeSecretMaterial: WorkspaceInternalToolContext["storeSecretMaterial"]; | |
| 213 | + deleteSecretMaterial: WorkspaceInternalToolContext["deleteSecretMaterial"]; | |
| 214 | + updateSecretMaterial: WorkspaceInternalToolContext["updateSecretMaterial"]; | |
| 204 | 215 | }) => |
| 205 | 216 | Layer.mergeAll( |
| 206 | 217 | Layer.succeed(ControlPlaneStore, input.controlPlaneStore), |
| @@ -209,6 +220,10 @@ const makeRuntimeLayer = (input: { · 2 unmodified lines | ||
| 209 | 220 | RuntimeSourceCatalogSyncService, |
| 210 | 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 | 227 | makeWorkspaceStorageLayer({ |
| 213 | 228 | workspaceConfigStore: input.workspaceConfigStore, |
| 214 | 229 | workspaceStateStore: input.workspaceStateStore, |
| @@ -220,8 +235,12 @@ const runRuntimeEffect = <A, E, R>(input: { · 5 unmodified lines | ||
| 220 | 235 | effect: Effect.Effect<A, E, R>; |
| 221 | 236 | runtimeLayer: Layer.Layer< |
| 222 | 237 | | ControlPlaneStore |
| 238 | + | LocalInstanceConfigService | |
| 223 | 239 | | RuntimeSourceStoreService |
| 224 | 240 | | RuntimeSourceCatalogSyncService |
| 241 | + | SecretMaterialStorerService | |
| 242 | + | SecretMaterialDeleterService | |
| 243 | + | SecretMaterialUpdaterService | |
| 225 | 244 | | WorkspaceConfigStore |
| 226 | 245 | | WorkspaceStateStore |
| 227 | 246 | | SourceArtifactStore, |
| @@ -263,6 +282,10 @@ export const createWorkspaceExecutorAdminToolMap = ( · 35 unmodified lines | ||
| 263 | 282 | workspaceConfigStore: input.workspaceConfigStore, |
| 264 | 283 | workspaceStateStore: input.workspaceStateStore, |
| 265 | 284 | sourceArtifactStore: input.sourceArtifactStore, |
| 285 | + instanceConfigResolver: input.instanceConfigResolver, | |
| 286 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 287 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 288 | + updateSecretMaterial: input.updateSecretMaterial, | |
| 266 | 289 | }); |
| 267 | 290 | const workspaceStorageLayer = makeWorkspaceStorageLayer({ |
| 268 | 291 | workspaceConfigStore: input.workspaceConfigStore, |
| @@ -295,7 +318,7 @@ export const createWorkspaceExecutorAdminToolMap = ( · 26 unmodified lines | ||
| 295 | 318 | "Get local instance config such as supported secret providers.", |
| 296 | 319 | inputSchema: emptyInputSchema, |
| 297 | 320 | outputSchema: instanceConfigOutputSchema, |
| 298 | − execute: () => Effect.runPromise(getLocalInstanceConfig()), | |
| 321 | + execute: () => Effect.runPromise(input.instanceConfigResolver()), | |
| 299 | 322 | }, |
| 300 | 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 | 6 | import * as Effect from "effect/Effect"; |
| 7 | 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 | 11 | import { |
| 12 | 12 | readOptionalLocalExecutorConfig, |
| @@ -37,7 +37,7 @@ type CaptureReleaseWorkspaceFixtureArgs = { · 24 unmodified lines | ||
| 37 | 37 | }; |
| 38 | 38 | |
| 39 | 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 | 41 | --workspace-root /path/to/workspace \\ |
| 42 | 42 | --source-id google-calendar \\ |
| 43 | 43 | --release-version v1.2.4-beta.1 \\ |
| @@ -58,7 +58,7 @@ describe("local-config", () => { · 57 unmodified lines | ||
| 58 | 58 | "https://api.github.com", |
| 59 | 59 | ); |
| 60 | 60 | expect(loaded.config?.runtime).toBe("ses"); |
| 61 | − expect(context.homeStateDirectory).toContain("executor"); | |
| 61 | + expect(context.homeStateDirectory.toLowerCase()).toContain("executor"); | |
| 62 | 62 | }).pipe(Effect.provide(NodeFileSystem.layer)), |
| 63 | 63 | ); |
| 64 | 64 | |
| @@ -13,9 +13,11 @@ import { · 12 unmodified lines | ||
| 13 | 13 | type LocalConfigPolicy, |
| 14 | 14 | type LocalConfigSecretProvider, |
| 15 | 15 | type LocalConfigSource, |
| 16 | −} from "#schema"; | |
| 16 | +} from "@executor/platform-sdk/schema"; | |
| 17 | 17 | import * as Effect from "effect/Effect"; |
| 18 | 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 | 21 | import { |
| 20 | 22 | LocalExecutorConfigDecodeError, |
| 21 | 23 | LocalFileSystemError, |
| @@ -24,6 +26,33 @@ import { · 2 unmodified lines | ||
| 24 | 26 | |
| 25 | 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 | 56 | const PROJECT_CONFIG_BASENAME = "executor.jsonc"; |
| 28 | 57 | const PROJECT_CONFIG_DIRECTORY = ".executor"; |
| 29 | 58 | const EXECUTOR_CONFIG_DIR_ENV = "EXECUTOR_CONFIG_DIR"; |
| @@ -329,14 +358,6 @@ export type ResolvedLocalWorkspaceContext = { · 299 unmodified lines | ||
| 329 | 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 | 361 | export const resolveLocalWorkspaceContext = (input: { |
| 341 | 362 | cwd?: string; |
| 342 | 363 | workspaceRoot?: string; |
| @@ -409,7 +430,7 @@ export const readOptionalLocalExecutorConfig = ( · 66 unmodified lines | ||
| 409 | 430 | export const loadLocalExecutorConfig = ( |
| 410 | 431 | context: ResolvedLocalWorkspaceContext, |
| 411 | 432 | ): Effect.Effect< |
| 412 | − LoadedLocalExecutorConfig, | |
| 433 | + FileLoadedExecutorConfig, | |
| 413 | 434 | LocalFileSystemError | LocalExecutorConfigDecodeError, |
| 414 | 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 | 452 | export const writeProjectLocalExecutorConfig = (input: { |
| 435 | 453 | context: ResolvedLocalWorkspaceContext; |
| 436 | 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 | 469 | export const defaultWorkspaceDisplayName = (context: ResolvedLocalWorkspaceContext): string => |
| 469 | 470 | trimOrUndefined(context.workspaceName) ?? "workspace"; |
File renamed without changes.
| @@ -24,7 +24,7 @@ import { · 23 unmodified lines | ||
| 24 | 24 | WorkspaceOauthClientSchema, |
| 25 | 25 | type WorkspaceSourceOauthClient, |
| 26 | 26 | WorkspaceSourceOauthClientSchema, |
| 27 | −} from "#schema"; | |
| 27 | +} from "@executor/platform-sdk/schema"; | |
| 28 | 28 | import * as Effect from "effect/Effect"; |
| 29 | 29 | import * as Option from "effect/Option"; |
| 30 | 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 | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | 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 | 9 | import { getOrProvisionLocalInstallation } from "./installation"; |
| 10 | 10 | import { resolveLocalWorkspaceContext } from "./config"; |
| 11 | 11 | |
| @@ -4,7 +4,7 @@ import { · 3 unmodified lines | ||
| 4 | 4 | AccountIdSchema, |
| 5 | 5 | WorkspaceIdSchema, |
| 6 | 6 | type LocalInstallation, |
| 7 | −} from "#schema"; | |
| 7 | +} from "@executor/platform-sdk/schema"; | |
| 8 | 8 | import * as Effect from "effect/Effect"; |
| 9 | 9 | |
| 10 | 10 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| @@ -28,7 +28,7 @@ export type ReleaseWorkspaceFixture = ReleaseWorkspaceFixtureManifest & { · 27 unmodified lines | ||
| 28 | 28 | }; |
| 29 | 29 | |
| 30 | 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 | 34 | const decodeReleaseWorkspaceFixtureManifest = Schema.decodeUnknownSync( |
| @@ -7,22 +7,34 @@ import { NodeFileSystem } from "@effect/platform-node"; · 6 unmodified lines | ||
| 7 | 7 | import { |
| 8 | 8 | type LocalConfigSecretProvider, |
| 9 | 9 | type LocalExecutorConfig, |
| 10 | − type SecretMaterial, | |
| 11 | 10 | type SecretMaterialPurpose, |
| 12 | − SecretMaterialIdSchema, | |
| 13 | 11 | type SecretRef, |
| 14 | −} from "#schema"; | |
| 15 | −import * as Context from "effect/Context"; | |
| 12 | + type SecretMaterial, | |
| 13 | + SecretMaterialIdSchema, | |
| 14 | +} from "@executor/platform-sdk/schema"; | |
| 16 | 15 | import * as Effect from "effect/Effect"; |
| 17 | 16 | import * as Layer from "effect/Layer"; |
| 18 | 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 | 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 | 39 | export const ENV_SECRET_PROVIDER_ID = "env"; |
| 28 | 40 | export const PARAMS_SECRET_PROVIDER_ID = "params"; |
| @@ -36,54 +48,6 @@ export type SecretStoreProviderId = · 7 unmodified lines | ||
| 36 | 48 | | typeof KEYCHAIN_SECRET_PROVIDER_ID |
| 37 | 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 | 51 | type SecretMaterialProviderRuntime = { |
| 88 | 52 | rows: ControlPlaneStoreShape; |
| 89 | 53 | env: NodeJS.ProcessEnv; |
| @@ -1085,17 +1049,16 @@ export const createDefaultSecretMaterialResolver = (input: { · 995 unmodified lines | ||
| 1085 | 1049 | |
| 1086 | 1050 | export const createDefaultSecretMaterialStorer = (input: { |
| 1087 | 1051 | rows: ControlPlaneStoreShape; |
| 1088 | − storeProviderId?: SecretStoreProviderId; | |
| 1089 | 1052 | dangerouslyAllowEnvSecrets?: boolean; |
| 1090 | 1053 | keychainServiceName?: string; |
| 1091 | 1054 | }): StoreSecretMaterial => { |
| 1092 | 1055 | const providers = createSecretMaterialProviderRegistry(); |
| 1093 | 1056 | const runtime = createSecretMaterialProviderRuntime(input); |
| 1094 | 1057 | |
| 1095 | − return ({ purpose, value, name }) => | |
| 1058 | + return ({ purpose, value, name, providerId }) => | |
| 1096 | 1059 | Effect.gen(function* () { |
| 1097 | 1060 | const defaultStoreProviderId = yield* resolveDefaultSecretStoreProviderId({ |
| 1098 | − storeProviderId: input.storeProviderId, | |
| 1061 | + storeProviderId: parseSecretStoreProviderId(providerId) ?? undefined, | |
| 1099 | 1062 | env: runtime.env, |
| 1100 | 1063 | }); |
| 1101 | 1064 | const provider = yield* getSecretMaterialProvider({ |
| @@ -1182,7 +1145,7 @@ const resolveRuntimeSecretMaterialConfig = (input: { · 80 unmodified lines | ||
| 1182 | 1145 | ?? runtimeLocalWorkspace?.loadedConfig.config |
| 1183 | 1146 | ?? null, |
| 1184 | 1147 | workspaceRoot: input.workspaceRoot |
| 1185 | − ?? runtimeLocalWorkspace?.context.workspaceRoot | |
| 1148 | + ?? runtimeLocalWorkspace?.workspace.workspaceRoot | |
| 1186 | 1149 | ?? null, |
| 1187 | 1150 | }; |
| 1188 | 1151 | }); |
| @@ -1213,7 +1176,6 @@ export const SecretMaterialResolverLive = (input: { · 24 unmodified lines | ||
| 1213 | 1176 | ); |
| 1214 | 1177 | |
| 1215 | 1178 | export const SecretMaterialStorerLive = (input: { |
| 1216 | − storeProviderId?: SecretStoreProviderId; | |
| 1217 | 1179 | dangerouslyAllowEnvSecrets?: boolean; |
| 1218 | 1180 | keychainServiceName?: string; |
| 1219 | 1181 | } = {}) => |
| @@ -1224,7 +1186,6 @@ export const SecretMaterialStorerLive = (input: { · 4 unmodified lines | ||
| 1224 | 1186 | |
| 1225 | 1187 | return createDefaultSecretMaterialStorer({ |
| 1226 | 1188 | rows, |
| 1227 | − storeProviderId: input.storeProviderId, | |
| 1228 | 1189 | dangerouslyAllowEnvSecrets: input.dangerouslyAllowEnvSecrets, |
| 1229 | 1190 | keychainServiceName: input.keychainServiceName, |
| 1230 | 1191 | }); |
| @@ -1267,7 +1228,6 @@ export const SecretMaterialUpdaterLive = (input: { · 36 unmodified lines | ||
| 1267 | 1228 | |
| 1268 | 1229 | export const SecretMaterialLive = (input: { |
| 1269 | 1230 | resolveSecretMaterial?: ResolveSecretMaterial; |
| 1270 | − storeProviderId?: SecretStoreProviderId; | |
| 1271 | 1231 | dangerouslyAllowEnvSecrets?: boolean; |
| 1272 | 1232 | keychainServiceName?: string; |
| 1273 | 1233 | localConfig?: LocalExecutorConfig | null; |
| @@ -1279,3 +1239,46 @@ export const SecretMaterialLive = (input: { · 5 unmodified lines | ||
| 1279 | 1239 | SecretMaterialDeleterLive(input), |
| 1280 | 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 | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | 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 | 14 | import { |
| 11 | 15 | buildLocalSourceArtifact, |
| @@ -76,7 +80,7 @@ const makeArtifact = () => { · 64 unmodified lines | ||
| 76 | 80 | documents: [ |
| 77 | 81 | { |
| 78 | 82 | documentKind: "openapi", |
| 79 | − documentKey: source.binding.specUrl, | |
| 83 | + documentKey: String(source.binding.specUrl), | |
| 80 | 84 | contentText: '{"openapi":"3.1.0"}', |
| 81 | 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 | 5 | import { describe, expect, it } from "@effect/vitest"; |
| 6 | 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 | 14 | import { |
| 11 | 15 | createCatalogSnapshotV1, |
| @@ -21,7 +25,7 @@ import { · 9 unmodified lines | ||
| 21 | 25 | ShapeSymbolIdSchema, |
| 22 | 26 | } from "@executor/ir/ids"; |
| 23 | 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 | 30 | const put = <K extends string, V>(record: Record<K, V>, key: K, value: V) => { |
| 27 | 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 | 15 | type ToolMetadata, |
| 16 | 16 | type ToolPath, |
| 17 | 17 | } from "@executor/codemode-core"; |
| 18 | −import * as Context from "effect/Context"; | |
| 19 | 18 | import * as Effect from "effect/Effect"; |
| 20 | −import * as Layer from "effect/Layer"; | |
| 21 | 19 | import * as ts from "typescript"; |
| 22 | 20 | |
| 21 | +import type { LocalToolRuntime } from "../../sdk/src/runtime/local-tool-runtime"; | |
| 23 | 22 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| 24 | 23 | import { |
| 25 | 24 | LocalFileSystemError, |
| @@ -79,13 +78,6 @@ export const defineLocalTool = < · 53 unmodified lines | ||
| 79 | 78 | | LocalToolWithMetadata<TInputSchema, TOutputSchema>, |
| 80 | 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 | 81 | const emptyLocalToolRuntime = (): LocalToolRuntime => { |
| 90 | 82 | const tools: ToolMap = {}; |
| 91 | 83 | return { |
| @@ -651,35 +643,3 @@ export const loadLocalToolRuntime = ( · 559 unmodified lines | ||
| 651 | 643 | toolPaths: new Set(Object.keys(tools)), |
| 652 | 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 | 1 | import { join } from "node:path"; |
| 2 | 2 | import { FileSystem } from "@effect/platform"; |
| 3 | − | |
| 4 | −import { | |
| 5 | − PolicyIdSchema, | |
| 6 | − SourceStatusSchema, | |
| 7 | − TimestampMsSchema, | |
| 8 | −} from "#schema"; | |
| 9 | 3 | import * as Effect from "effect/Effect"; |
| 10 | 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 | 11 | import type { ResolvedLocalWorkspaceContext } from "./config"; |
| 13 | 12 | import { |
| 14 | 13 | LocalFileSystemError, |
| @@ -18,36 +17,6 @@ import { · 3 unmodified lines | ||
| 18 | 17 | |
| 19 | 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 | 20 | const decodeLocalWorkspaceState = Schema.decodeUnknownSync(LocalWorkspaceStateSchema); |
| 52 | 21 | |
| 53 | 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 | 27 | details: unknownLocalErrorDetails(cause), |
| 59 | 28 | }); |
| 60 | 29 | |
| 61 | −const defaultLocalWorkspaceState = (): LocalWorkspaceState => ({ | |
| 62 | − version: 1, | |
| 63 | − sources: {}, | |
| 64 | − policies: {}, | |
| 65 | −}); | |
| 66 | − | |
| 67 | 30 | export const localWorkspaceStatePath = ( |
| 68 | 31 | context: ResolvedLocalWorkspaceContext, |
| 69 | 32 | ): string => join(context.stateDirectory, WORKSPACE_STATE_BASENAME); |
| @@ -90,13 +53,12 @@ export const loadLocalWorkspaceState = ( · 20 unmodified lines | ||
| 90 | 53 | ); |
| 91 | 54 | return yield* Effect.try({ |
| 92 | 55 | try: () => decodeLocalWorkspaceState(JSON.parse(content) as unknown), |
| 93 | − catch: (cause) => { | |
| 94 | − return new LocalWorkspaceStateDecodeError({ | |
| 56 | + catch: (cause) => | |
| 57 | + new LocalWorkspaceStateDecodeError({ | |
| 95 | 58 | message: `Invalid local workspace state at ${path}: ${unknownLocalErrorDetails(cause)}`, |
| 96 | 59 | path, |
| 97 | 60 | details: unknownLocalErrorDetails(cause), |
| 98 | − }); | |
| 99 | − }, | |
| 61 | + }), | |
| 100 | 62 | }); |
| 101 | 63 | }); |
| 102 | 64 | |
| @@ -107,12 +69,12 @@ export const writeLocalWorkspaceState = (input: { · 4 unmodified lines | ||
| 107 | 69 | Effect.gen(function* () { |
| 108 | 70 | const fs = yield* FileSystem.FileSystem; |
| 109 | 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( | |
| 113 | − localWorkspaceStatePath(input.context), | |
| 114 | − `${JSON.stringify(input.state, null, 2)}\n`, | |
| 115 | − ).pipe( | |
| 116 | − Effect.mapError(mapFileSystemError(localWorkspaceStatePath(input.context), "write workspace state")), | |
| 76 | + const path = localWorkspaceStatePath(input.context); | |
| 77 | + yield* fs.writeFileString(path, `${JSON.stringify(input.state, null, 2)}\n`).pipe( | |
| 78 | + Effect.mapError(mapFileSystemError(path, "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 | 18 | "#schema": "./src/schema/index.ts" |
| 19 | 19 | }, |
| 20 | 20 | "scripts": { |
| 21 | − "fixture:release:capture": "bun run ./src/runtime/local/capture-release-workspace-fixture.ts", | |
| 22 | 21 | "test": "vitest run", |
| 23 | 22 | "test:watch": "vitest", |
| 24 | 23 | "typecheck": "bunx tsc --noEmit -p tsconfig.json" |
| @@ -12,6 +12,10 @@ import { · 11 unmodified lines | ||
| 12 | 12 | type RuntimeControlPlaneServices, |
| 13 | 13 | type RuntimeSecretMaterialServices, |
| 14 | 14 | } from "./runtime"; |
| 15 | +export type { | |
| 16 | + ExecutorWorkspaceContext, | |
| 17 | + ExecutorWorkspaceDescriptor, | |
| 18 | +} from "./workspace"; | |
| 15 | 19 | |
| 16 | 20 | export type ExecutorBackend = { |
| 17 | 21 | createRuntime: ( |
| @@ -14,12 +14,7 @@ import * as Option from "effect/Option"; · 13 unmodified lines | ||
| 14 | 14 | import { submitExecutionInteractionResponse } from "../runtime/execution/service"; |
| 15 | 15 | import { LiveExecutionManagerService } from "../runtime/execution/live"; |
| 16 | 16 | import { operationErrors } from "../runtime/policy/operation-errors"; |
| 17 | −import { resolveLocalWorkspaceContext } from "../runtime/local/config"; | |
| 18 | −import { | |
| 19 | − InstallationStore, | |
| 20 | − LocalInstallationStore, | |
| 21 | −} from "../runtime/local/storage"; | |
| 22 | −import { getRuntimeLocalWorkspaceOption } from "../runtime/local/runtime-context"; | |
| 17 | +import { InstallationStore } from "../runtime/workspace/storage"; | |
| 23 | 18 | import { RuntimeSourceAuthServiceTag } from "../runtime/sources/source-auth-service"; |
| 24 | 19 | import { |
| 25 | 20 | createSourceCredentialSelectionBearerContent, |
| @@ -190,24 +185,15 @@ const loadSourceCredentialInteraction = (input: { · 164 unmodified lines | ||
| 190 | 185 | |
| 191 | 186 | export const getLocalInstallation = () => |
| 192 | 187 | Effect.gen(function* () { |
| 193 | − const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); | |
| 194 | − const context = | |
| 195 | − runtimeLocalWorkspace?.context ?? | |
| 196 | − (yield* resolveLocalWorkspaceContext().pipe( | |
| 197 | − Effect.mapError((error) => | |
| 198 | − localOps.installation.unknownStorage( | |
| 199 | − error, | |
| 200 | − "Failed resolving local workspace context", | |
| 201 | − ), | |
| 188 | + const installationStore = yield* InstallationStore; | |
| 189 | + return yield* installationStore.load().pipe( | |
| 190 | + Effect.mapError((error) => | |
| 191 | + localOps.installation.unknownStorage( | |
| 192 | + error, | |
| 193 | + "Failed loading local installation", | |
| 202 | 194 | ), |
| 203 | − )); | |
| 204 | − | |
| 205 | − const installationStore = | |
| 206 | − runtimeLocalWorkspace !== null | |
| 207 | − ? yield* InstallationStore | |
| 208 | − : LocalInstallationStore; | |
| 209 | − | |
| 210 | − return yield* installationStore.load(context); | |
| 195 | + ), | |
| 196 | + ); | |
| 211 | 197 | }); |
| 212 | 198 | |
| 213 | 199 | export const getSourceCredentialInteraction = (input: { |
| @@ -16,22 +16,16 @@ import { · 15 unmodified lines | ||
| 16 | 16 | ControlPlaneNotFoundError, |
| 17 | 17 | ControlPlaneStorageError, |
| 18 | 18 | } from "../errors"; |
| 19 | −import { requireRuntimeLocalWorkspace } from "../runtime/local/runtime-context"; | |
| 19 | +import { requireRuntimeLocalWorkspace } from "../runtime/workspace/runtime-context"; | |
| 20 | 20 | import { |
| 21 | − createDefaultSecretMaterialDeleter, | |
| 22 | − createDefaultSecretMaterialStorer, | |
| 23 | − createDefaultSecretMaterialUpdater, | |
| 24 | − ENV_SECRET_PROVIDER_ID, | |
| 25 | − KEYCHAIN_SECRET_PROVIDER_ID, | |
| 26 | − LOCAL_SECRET_PROVIDER_ID, | |
| 27 | − parseSecretStoreProviderId, | |
| 28 | − resolveDefaultSecretStoreProviderId, | |
| 29 | −} from "../runtime/local/secret-material-providers"; | |
| 21 | + LocalInstanceConfigService, | |
| 22 | + SecretMaterialDeleterService, | |
| 23 | + SecretMaterialStorerService, | |
| 24 | + SecretMaterialUpdaterService, | |
| 25 | +} from "../runtime/workspace/secret-material-providers"; | |
| 30 | 26 | import { RuntimeSourceStoreService } from "../runtime/sources/source-store"; |
| 31 | 27 | import { ControlPlaneStore } from "../runtime/store"; |
| 32 | 28 | |
| 33 | −const SECRET_STORE_PROVIDER_ENV = "EXECUTOR_SECRET_STORE_PROVIDER"; | |
| 34 | − | |
| 35 | 29 | const secretStorageError = (operation: string, message: string) => |
| 36 | 30 | new ControlPlaneStorageError({ |
| 37 | 31 | operation, |
| @@ -39,45 +33,8 @@ const secretStorageError = (operation: string, message: string) => · 1 unmodified line | ||
| 39 | 33 | details: message, |
| 40 | 34 | }); |
| 41 | 35 | |
| 42 | −export const getLocalInstanceConfig = (): Effect.Effect<InstanceConfig> => { | |
| 43 | − const explicitDefaultStoreProvider = parseSecretStoreProviderId( | |
| 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 | −}; | |
| 36 | +export const getLocalInstanceConfig = (): Effect.Effect<InstanceConfig, Error, LocalInstanceConfigService> => | |
| 37 | + Effect.flatMap(LocalInstanceConfigService, (resolveInstanceConfig) => resolveInstanceConfig()); | |
| 81 | 38 | |
| 82 | 39 | export const listLocalSecrets = () => |
| 83 | 40 | Effect.gen(function* () { |
| @@ -119,10 +76,6 @@ export const createLocalSecret = (payload: CreateSecretPayload) => · 35 unmodified lines | ||
| 119 | 76 | const name = payload.name.trim(); |
| 120 | 77 | const value = payload.value; |
| 121 | 78 | const purpose = payload.purpose ?? "auth_material"; |
| 122 | − const requestedProviderId = | |
| 123 | − payload.providerId === undefined | |
| 124 | − ? null | |
| 125 | − : parseSecretStoreProviderId(payload.providerId); | |
| 126 | 79 | |
| 127 | 80 | if (name.length === 0) { |
| 128 | 81 | return yield* new ControlPlaneBadRequestError({ |
| @@ -131,23 +84,14 @@ export const createLocalSecret = (payload: CreateSecretPayload) => · 2 unmodified lines | ||
| 131 | 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 | 88 | const store = yield* ControlPlaneStore; |
| 143 | − const storeSecretMaterial = createDefaultSecretMaterialStorer({ | |
| 144 | − rows: store, | |
| 145 | − ...(requestedProviderId ? { storeProviderId: requestedProviderId } : {}), | |
| 146 | − }); | |
| 89 | + const storeSecretMaterial = yield* SecretMaterialStorerService; | |
| 147 | 90 | const ref = yield* storeSecretMaterial({ |
| 148 | 91 | name, |
| 149 | 92 | purpose, |
| 150 | 93 | value, |
| 94 | + providerId: payload.providerId, | |
| 151 | 95 | }).pipe( |
| 152 | 96 | Effect.mapError((cause) => |
| 153 | 97 | secretStorageError( |
| @@ -214,9 +158,7 @@ export const updateLocalSecret = (input: { · 60 unmodified lines | ||
| 214 | 158 | update.name = input.payload.name.trim() || null; |
| 215 | 159 | if (input.payload.value !== undefined) update.value = input.payload.value; |
| 216 | 160 | |
| 217 | − const updateSecretMaterial = createDefaultSecretMaterialUpdater({ | |
| 218 | − rows: store, | |
| 219 | − }); | |
| 161 | + const updateSecretMaterial = yield* SecretMaterialUpdaterService; | |
| 220 | 162 | const updated = yield* updateSecretMaterial({ |
| 221 | 163 | ref: { |
| 222 | 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({ | |
| 264 | − rows: store, | |
| 265 | − }); | |
| 205 | + const deleteSecretMaterial = yield* SecretMaterialDeleterService; | |
| 266 | 206 | const removed = yield* deleteSecretMaterial({ |
| 267 | 207 | providerId: existing.value.providerId, |
| 268 | 208 | handle: existing.value.id, |
| @@ -13,18 +13,17 @@ import { · 12 unmodified lines | ||
| 13 | 13 | } from "../schema"; |
| 14 | 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 | 17 | import type { |
| 18 | 18 | WorkspaceConfigStoreShape, |
| 19 | 19 | WorkspaceStateStoreShape, |
| 20 | −} from "../runtime/local/storage"; | |
| 20 | +} from "../runtime/workspace/storage"; | |
| 21 | 21 | import { |
| 22 | 22 | WorkspaceConfigStore, |
| 23 | 23 | WorkspaceStateStore, |
| 24 | −} from "../runtime/local/storage"; | |
| 25 | −import type { ResolvedLocalWorkspaceContext } from "../runtime/local/config"; | |
| 26 | −import { type LocalWorkspaceState } from "../runtime/local/workspace-state"; | |
| 27 | −import { derivePolicyConfigKey } from "../runtime/local/workspace-sync"; | |
| 24 | +} from "../runtime/workspace/storage"; | |
| 25 | +import { type LocalWorkspaceState } from "../runtime/workspace-state"; | |
| 26 | +import { derivePolicyConfigKey } from "../runtime/workspace/workspace-sync"; | |
| 28 | 27 | import { |
| 29 | 28 | type OperationErrors, |
| 30 | 29 | operationErrors, |
| @@ -40,17 +39,22 @@ const policyOps = { · 9 unmodified lines | ||
| 40 | 39 | |
| 41 | 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 | 47 | const localPolicyIdForKey = (input: { |
| 44 | − workspaceRoot: string; | |
| 48 | + workspaceStableKey: string; | |
| 45 | 49 | key: string; |
| 46 | 50 | }): PolicyId => |
| 47 | 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 | 55 | const toLocalWorkspacePolicy = (input: { |
| 52 | 56 | workspaceId: WorkspaceId; |
| 53 | − workspaceRoot: string; | |
| 57 | + workspaceStableKey: string; | |
| 54 | 58 | key: string; |
| 55 | 59 | policyConfig: NonNullable<LocalExecutorConfig["policies"]>[string]; |
| 56 | 60 | state: LocalWorkspaceState["policies"][string] | undefined; |
| @@ -58,7 +62,7 @@ const toLocalWorkspacePolicy = (input: { · 1 unmodified line | ||
| 58 | 62 | id: |
| 59 | 63 | input.state?.id ?? |
| 60 | 64 | localPolicyIdForKey({ |
| 61 | − workspaceRoot: input.workspaceRoot, | |
| 65 | + workspaceStableKey: input.workspaceStableKey, | |
| 62 | 66 | key: input.key, |
| 63 | 67 | }), |
| 64 | 68 | key: input.key, |
| @@ -78,18 +82,17 @@ export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => · 13 unmodified lines | ||
| 78 | 82 | yield* requireRuntimeLocalWorkspace(workspaceId); |
| 79 | 83 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 80 | 84 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 81 | − const loadedConfig = yield* workspaceConfigStore.load( | |
| 82 | − runtimeLocalWorkspace.context, | |
| 83 | − ); | |
| 84 | − const workspaceState = yield* workspaceStateStore.load( | |
| 85 | − runtimeLocalWorkspace.context, | |
| 86 | − ); | |
| 85 | + const loadedConfig = yield* workspaceConfigStore.load(); | |
| 86 | + const workspaceState = yield* workspaceStateStore.load(); | |
| 87 | 87 | |
| 88 | 88 | const policies = Object.entries(loadedConfig.config?.policies ?? {}).map( |
| 89 | 89 | ([key, policyConfig]) => |
| 90 | 90 | toLocalWorkspacePolicy({ |
| 91 | 91 | workspaceId, |
| 92 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 92 | + workspaceStableKey: workspacePolicyStableKey({ | |
| 93 | + workspaceId, | |
| 94 | + workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | |
| 95 | + }), | |
| 93 | 96 | key, |
| 94 | 97 | policyConfig, |
| 95 | 98 | state: workspaceState.policies[key], |
| @@ -106,7 +109,6 @@ export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => · 10 unmodified lines | ||
| 106 | 109 | |
| 107 | 110 | const writeLocalPolicyFiles = (input: { |
| 108 | 111 | operation: OperationErrors; |
| 109 | − context: ResolvedLocalWorkspaceContext; | |
| 110 | 112 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 111 | 113 | workspaceStateStore: WorkspaceStateStoreShape; |
| 112 | 114 | projectConfig: LocalExecutorConfig; |
| @@ -115,11 +117,9 @@ const writeLocalPolicyFiles = (input: { · 2 unmodified lines | ||
| 115 | 117 | Effect.all( |
| 116 | 118 | [ |
| 117 | 119 | input.workspaceConfigStore.writeProject({ |
| 118 | − context: input.context, | |
| 119 | 120 | config: input.projectConfig, |
| 120 | 121 | }), |
| 121 | 122 | input.workspaceStateStore.write({ |
| 122 | − context: input.context, | |
| 123 | 123 | state: input.workspaceState, |
| 124 | 124 | }), |
| 125 | 125 | ], |
| @@ -219,7 +219,10 @@ export const createPolicy = (input: { · 93 unmodified lines | ||
| 219 | 219 | id: |
| 220 | 220 | existingState?.id ?? |
| 221 | 221 | localPolicyIdForKey({ |
| 222 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 222 | + workspaceStableKey: workspacePolicyStableKey({ | |
| 223 | + workspaceId: input.workspaceId, | |
| 224 | + workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | |
| 225 | + }), | |
| 223 | 226 | key, |
| 224 | 227 | }), |
| 225 | 228 | createdAt: existingState?.createdAt ?? now, |
| @@ -230,7 +233,6 @@ export const createPolicy = (input: { · 4 unmodified lines | ||
| 230 | 233 | |
| 231 | 234 | yield* writeLocalPolicyFiles({ |
| 232 | 235 | operation: policyOps.create, |
| 233 | − context: runtimeLocalWorkspace.context, | |
| 234 | 236 | workspaceConfigStore, |
| 235 | 237 | workspaceStateStore, |
| 236 | 238 | projectConfig: { |
| @@ -242,7 +244,10 @@ export const createPolicy = (input: { · 5 unmodified lines | ||
| 242 | 244 | |
| 243 | 245 | return toLocalWorkspacePolicy({ |
| 244 | 246 | workspaceId: input.workspaceId, |
| 245 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 247 | + workspaceStableKey: workspacePolicyStableKey({ | |
| 248 | + workspaceId: input.workspaceId, | |
| 249 | + workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | |
| 250 | + }), | |
| 246 | 251 | key, |
| 247 | 252 | policyConfig: policies[key]!, |
| 248 | 253 | state: workspaceState.policies[key], |
| @@ -355,7 +360,6 @@ export const updatePolicy = (input: { · 106 unmodified lines | ||
| 355 | 360 | |
| 356 | 361 | yield* writeLocalPolicyFiles({ |
| 357 | 362 | operation: policyOps.update, |
| 358 | − context: runtimeLocalWorkspace.context, | |
| 359 | 363 | workspaceConfigStore, |
| 360 | 364 | workspaceStateStore, |
| 361 | 365 | projectConfig: { |
| @@ -367,7 +371,10 @@ export const updatePolicy = (input: { · 5 unmodified lines | ||
| 367 | 371 | |
| 368 | 372 | return toLocalWorkspacePolicy({ |
| 369 | 373 | workspaceId: input.workspaceId, |
| 370 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 374 | + workspaceStableKey: workspacePolicyStableKey({ | |
| 375 | + workspaceId: input.workspaceId, | |
| 376 | + workspaceRoot: runtimeLocalWorkspace.workspace.workspaceRoot, | |
| 377 | + }), | |
| 371 | 378 | key: existing.key, |
| 372 | 379 | policyConfig: policies[existing.key]!, |
| 373 | 380 | state: workspaceState.policies[existing.key], |
| @@ -413,7 +420,6 @@ export const removePolicy = (input: { · 39 unmodified lines | ||
| 413 | 420 | localWorkspace.workspaceState.policies; |
| 414 | 421 | yield* writeLocalPolicyFiles({ |
| 415 | 422 | operation: policyOps.remove, |
| 416 | − context: runtimeLocalWorkspace.context, | |
| 417 | 423 | workspaceConfigStore, |
| 418 | 424 | workspaceStateStore, |
| 419 | 425 | projectConfig: { |
| @@ -9,7 +9,7 @@ Each fixture directory should contain: · 8 unmodified lines | ||
| 9 | 9 | - `.executor/state/workspace-state.json`: a trimmed workspace state with only the captured source |
| 10 | 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 | 14 | ## Capture A New Release Fixture |
| 15 | 15 | |
| @@ -35,4 +35,4 @@ On releases where you want upgrade coverage: · 19 unmodified lines | ||
| 35 | 35 | 1. Start from a real workspace that has a fully synced source on the released version. |
| 36 | 36 | 2. Run `bun run fixture:release:capture -- ...` for that source. |
| 37 | 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 | 35 | import type { |
| 36 | 36 | ResolveSecretMaterial, |
| 37 | 37 | SecretMaterialResolveContext, |
| 38 | −} from "../local/secret-material-providers"; | |
| 38 | +} from "../workspace/secret-material-providers"; | |
| 39 | 39 | import { runtimeEffectError } from "../effect-errors"; |
| 40 | 40 | |
| 41 | 41 | export type ResolvedSourceAuthMaterial = { |
| @@ -19,11 +19,11 @@ import * as Option from "effect/Option"; · 18 unmodified lines | ||
| 19 | 19 | import type { ControlPlaneStoreShape } from "../store"; |
| 20 | 20 | import { resolveAuthArtifactMaterial, type ResolvedSourceAuthMaterial } from "./auth-artifacts"; |
| 21 | 21 | import { |
| 22 | − createDefaultSecretMaterialDeleter, | |
| 23 | − createDefaultSecretMaterialStorer, | |
| 22 | + type DeleteSecretMaterial, | |
| 24 | 23 | type ResolveSecretMaterial, |
| 25 | 24 | type SecretMaterialResolveContext, |
| 26 | −} from "../local/secret-material-providers"; | |
| 25 | + type StoreSecretMaterial, | |
| 26 | +} from "../workspace/secret-material-providers"; | |
| 27 | 27 | import { refreshOAuth2AccessToken } from "./oauth2-pkce"; |
| 28 | 28 | import type { OAuth2TokenResponse } from "./oauth2-pkce"; |
| 29 | 29 | import { createPersistedMcpAuthProvider } from "./mcp-auth-provider"; |
| @@ -43,13 +43,12 @@ const cleanupAuthLeaseSecretRefs = ( · 13 unmodified lines | ||
| 43 | 43 | previous: Pick<AuthLease, "placementsTemplateJson"> | null; |
| 44 | 44 | next: Pick<AuthLease, "placementsTemplateJson"> | null; |
| 45 | 45 | }, |
| 46 | + deleteSecretMaterial: DeleteSecretMaterial, | |
| 46 | 47 | ) => |
| 47 | 48 | Effect.gen(function* () { |
| 48 | 49 | if (input.previous === null) { |
| 49 | 50 | return; |
| 50 | 51 | } |
| 51 | − | |
| 52 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | |
| 53 | 52 | const nextRefKeys = new Set( |
| 54 | 53 | (input.next === null ? [] : authLeaseSecretRefs(input.next)).map(secretRefKey), |
| 55 | 54 | ); |
| @@ -85,6 +84,8 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 29 unmodified lines | ||
| 85 | 84 | artifact: AuthArtifact; |
| 86 | 85 | lease: AuthLease | null; |
| 87 | 86 | resolveSecretMaterial: ResolveSecretMaterial; |
| 87 | + storeSecretMaterial: StoreSecretMaterial; | |
| 88 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 88 | 89 | context?: SecretMaterialResolveContext; |
| 89 | 90 | }): Effect.Effect<AuthLease, Error, never> => |
| 90 | 91 | Effect.gen(function* () { |
| @@ -116,10 +117,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 25 unmodified lines | ||
| 116 | 117 | refreshToken, |
| 117 | 118 | }); |
| 118 | 119 | |
| 119 | − const storeSecretMaterial = createDefaultSecretMaterialStorer({ | |
| 120 | − rows: input.rows, | |
| 121 | − }); | |
| 122 | − const accessTokenRef = yield* storeSecretMaterial({ | |
| 120 | + const accessTokenRef = yield* input.storeSecretMaterial({ | |
| 123 | 121 | purpose: "oauth_access_token", |
| 124 | 122 | value: tokenResponse.access_token, |
| 125 | 123 | name: `${decoded.config.clientId} Access Token`, |
| @@ -166,7 +164,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 40 unmodified lines | ||
| 166 | 164 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 167 | 165 | previous: input.lease, |
| 168 | 166 | next: nextLease, |
| 169 | − }); | |
| 167 | + }, input.deleteSecretMaterial); | |
| 170 | 168 | |
| 171 | 169 | return nextLease; |
| 172 | 170 | }); |
| @@ -185,10 +183,10 @@ const workspaceOauthClientSecretRef = (input: { · 12 unmodified lines | ||
| 185 | 183 | const cleanupProviderGrantRefreshToken = (rows: ControlPlaneStoreShape, input: { |
| 186 | 184 | previous: SecretRef; |
| 187 | 185 | next: SecretRef; |
| 188 | −}) => | |
| 186 | +}, deleteSecretMaterial: DeleteSecretMaterial) => | |
| 189 | 187 | secretRefKey(input.previous) === secretRefKey(input.next) |
| 190 | 188 | ? Effect.void |
| 191 | − : createDefaultSecretMaterialDeleter({ rows })(input.previous).pipe( | |
| 189 | + : deleteSecretMaterial(input.previous).pipe( | |
| 192 | 190 | Effect.either, |
| 193 | 191 | Effect.ignore, |
| 194 | 192 | ); |
| @@ -198,6 +196,8 @@ const refreshProviderGrantRefArtifact = (input: { · 3 unmodified lines | ||
| 198 | 196 | artifact: AuthArtifact; |
| 199 | 197 | lease: AuthLease | null; |
| 200 | 198 | resolveSecretMaterial: ResolveSecretMaterial; |
| 199 | + storeSecretMaterial: StoreSecretMaterial; | |
| 200 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 201 | 201 | context?: SecretMaterialResolveContext; |
| 202 | 202 | }): Effect.Effect<AuthLease, Error, never> => |
| 203 | 203 | Effect.gen(function* () { |
| @@ -239,17 +239,14 @@ const refreshProviderGrantRefArtifact = (input: { · 35 unmodified lines | ||
| 239 | 239 | scopes: config.requiredScopes.length > 0 ? config.requiredScopes : null, |
| 240 | 240 | }); |
| 241 | 241 | |
| 242 | − const storeSecretMaterial = createDefaultSecretMaterialStorer({ | |
| 243 | − rows: input.rows, | |
| 244 | − }); | |
| 245 | − const accessTokenRef = yield* storeSecretMaterial({ | |
| 242 | + const accessTokenRef = yield* input.storeSecretMaterial({ | |
| 246 | 243 | purpose: "oauth_access_token", |
| 247 | 244 | value: tokenResponse.access_token, |
| 248 | 245 | name: `${grant.providerKey} Access Token`, |
| 249 | 246 | }); |
| 250 | 247 | |
| 251 | 248 | const rotatedRefreshTokenRef = tokenResponse.refresh_token |
| 252 | − ? yield* storeSecretMaterial({ | |
| 249 | + ? yield* input.storeSecretMaterial({ | |
| 253 | 250 | purpose: "oauth_refresh_token", |
| 254 | 251 | value: tokenResponse.refresh_token, |
| 255 | 252 | name: `${grant.providerKey} Refresh Token`, |
| @@ -267,7 +264,7 @@ const refreshProviderGrantRefArtifact = (input: { · 11 unmodified lines | ||
| 267 | 264 | yield* cleanupProviderGrantRefreshToken(input.rows, { |
| 268 | 265 | previous: grant.refreshToken, |
| 269 | 266 | next: rotatedRefreshTokenRef, |
| 270 | − }); | |
| 267 | + }, input.deleteSecretMaterial); | |
| 271 | 268 | |
| 272 | 269 | const expiresInMs = |
| 273 | 270 | typeof tokenResponse.expires_in === "number" && Number.isFinite(tokenResponse.expires_in) |
| @@ -310,14 +307,14 @@ const refreshProviderGrantRefArtifact = (input: { · 36 unmodified lines | ||
| 310 | 307 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 311 | 308 | previous: input.lease, |
| 312 | 309 | next: nextLease, |
| 313 | − }); | |
| 310 | + }, input.deleteSecretMaterial); | |
| 314 | 311 | |
| 315 | 312 | return nextLease; |
| 316 | 313 | }); |
| 317 | 314 | |
| 318 | 315 | export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { |
| 319 | 316 | authArtifactId: AuthArtifact["id"]; |
| 320 | −}): Effect.Effect<void, Error, never> => | |
| 317 | +}, deleteSecretMaterial: DeleteSecretMaterial): Effect.Effect<void, Error, never> => | |
| 321 | 318 | Effect.gen(function* () { |
| 322 | 319 | const existingLease = yield* rows.authLeases.getByAuthArtifactId(input.authArtifactId); |
| 323 | 320 | if (Option.isNone(existingLease)) { |
| @@ -328,13 +325,15 @@ export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { · 4 unmodified lines | ||
| 328 | 325 | yield* cleanupAuthLeaseSecretRefs(rows, { |
| 329 | 326 | previous: existingLease.value, |
| 330 | 327 | next: null, |
| 331 | − }); | |
| 328 | + }, deleteSecretMaterial); | |
| 332 | 329 | }); |
| 333 | 330 | |
| 334 | 331 | export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { |
| 335 | 332 | rows: ControlPlaneStoreShape; |
| 336 | 333 | artifact: AuthArtifact; |
| 337 | 334 | tokenResponse: OAuth2TokenResponse; |
| 335 | + storeSecretMaterial: StoreSecretMaterial; | |
| 336 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 338 | 337 | }): Effect.Effect<void, Error, never> => |
| 339 | 338 | Effect.gen(function* () { |
| 340 | 339 | const decoded = decodeBuiltInAuthArtifactConfig(input.artifact); |
| @@ -347,10 +346,7 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 6 unmodified lines | ||
| 347 | 346 | |
| 348 | 347 | const existingLeaseOption = yield* input.rows.authLeases.getByAuthArtifactId(input.artifact.id); |
| 349 | 348 | const existingLease = Option.isSome(existingLeaseOption) ? existingLeaseOption.value : null; |
| 350 | − const storeSecretMaterial = createDefaultSecretMaterialStorer({ | |
| 351 | − rows: input.rows, | |
| 352 | − }); | |
| 353 | − const accessTokenRef = yield* storeSecretMaterial({ | |
| 349 | + const accessTokenRef = yield* input.storeSecretMaterial({ | |
| 354 | 350 | purpose: "oauth_access_token", |
| 355 | 351 | value: input.tokenResponse.access_token, |
| 356 | 352 | name: `${decoded.config.clientId} Access Token`, |
| @@ -397,13 +393,15 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 40 unmodified lines | ||
| 397 | 393 | yield* cleanupAuthLeaseSecretRefs(input.rows, { |
| 398 | 394 | previous: existingLease, |
| 399 | 395 | next: nextLease, |
| 400 | − }); | |
| 396 | + }, input.deleteSecretMaterial); | |
| 401 | 397 | }); |
| 402 | 398 | |
| 403 | 399 | export const resolveAuthArtifactMaterialWithLeases = (input: { |
| 404 | 400 | rows: ControlPlaneStoreShape; |
| 405 | 401 | artifact: AuthArtifact | null; |
| 406 | 402 | resolveSecretMaterial: ResolveSecretMaterial; |
| 403 | + storeSecretMaterial: StoreSecretMaterial; | |
| 404 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 407 | 405 | context?: SecretMaterialResolveContext; |
| 408 | 406 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => |
| 409 | 407 | Effect.gen(function* () { |
| @@ -432,6 +430,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 22 unmodified lines | ||
| 432 | 430 | artifact: input.artifact, |
| 433 | 431 | lease: existingLease, |
| 434 | 432 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 433 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 434 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 435 | 435 | context: input.context, |
| 436 | 436 | }); |
| 437 | 437 | |
| @@ -451,6 +451,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 13 unmodified lines | ||
| 451 | 451 | artifact: input.artifact, |
| 452 | 452 | lease: existingLease, |
| 453 | 453 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 454 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 455 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 454 | 456 | context: input.context, |
| 455 | 457 | }); |
| 456 | 458 | |
| @@ -476,6 +478,8 @@ export const resolveAuthArtifactMaterialWithLeases = (input: { · 19 unmodified lines | ||
| 476 | 478 | artifact: input.artifact, |
| 477 | 479 | config: mcpOAuthConfig, |
| 478 | 480 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 481 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 482 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 479 | 483 | context: input.context, |
| 480 | 484 | }), |
| 481 | 485 | }; |
| @@ -22,11 +22,11 @@ import * as Schema from "effect/Schema"; · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | import type { ControlPlaneStoreShape } from "../store"; |
| 24 | 24 | import { |
| 25 | − createDefaultSecretMaterialDeleter, | |
| 26 | − createDefaultSecretMaterialStorer, | |
| 25 | + type DeleteSecretMaterial, | |
| 27 | 26 | type ResolveSecretMaterial, |
| 28 | 27 | type SecretMaterialResolveContext, |
| 29 | −} from "../local/secret-material-providers"; | |
| 28 | + type StoreSecretMaterial, | |
| 29 | +} from "../workspace/secret-material-providers"; | |
| 30 | 30 | |
| 31 | 31 | const encodeMcpOAuthAuthArtifactConfig = Schema.encodeSync( |
| 32 | 32 | McpOAuthAuthArtifactConfigJsonSchema, |
| @@ -62,24 +62,23 @@ const secretRefEquals = (left: SecretRef | null, right: SecretRef | null): boole · 29 unmodified lines | ||
| 62 | 62 | (left?.providerId ?? null) === (right?.providerId ?? null) |
| 63 | 63 | && (left?.handle ?? null) === (right?.handle ?? null); |
| 64 | 64 | |
| 65 | −const cleanupReplacedSecretRefs = (rows: ControlPlaneStoreShape, input: { | |
| 65 | +const cleanupReplacedSecretRefs = (input: { | |
| 66 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 66 | 67 | previousAccessToken: SecretRef; |
| 67 | 68 | nextAccessToken: SecretRef; |
| 68 | 69 | previousRefreshToken: SecretRef | null; |
| 69 | 70 | nextRefreshToken: SecretRef | null; |
| 70 | 71 | }) => |
| 71 | 72 | Effect.gen(function* () { |
| 72 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | |
| 73 | − | |
| 74 | 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 | 77 | if ( |
| 79 | 78 | input.previousRefreshToken !== null |
| 80 | 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 | 114 | artifact: AuthArtifact; |
| 116 | 115 | config: McpOAuthAuthArtifactConfig; |
| 117 | 116 | resolveSecretMaterial: ResolveSecretMaterial; |
| 117 | + storeSecretMaterial: StoreSecretMaterial; | |
| 118 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 118 | 119 | context?: SecretMaterialResolveContext; |
| 119 | 120 | }): OAuthClientProvider => { |
| 120 | − const storeSecretMaterial = createDefaultSecretMaterialStorer({ | |
| 121 | − rows: input.rows, | |
| 122 | − }); | |
| 123 | − | |
| 124 | 121 | let currentArtifact = input.artifact; |
| 125 | 122 | let currentConfig = input.config; |
| 126 | 123 | |
| @@ -186,13 +183,13 @@ export const createPersistedMcpAuthProvider = (input: { · 59 unmodified lines | ||
| 186 | 183 | saveTokens: (tokens) => |
| 187 | 184 | Effect.runPromise(Effect.gen(function* () { |
| 188 | 185 | const previousConfig = currentConfig; |
| 189 | − const accessTokenRef = yield* storeSecretMaterial({ | |
| 186 | + const accessTokenRef = yield* input.storeSecretMaterial({ | |
| 190 | 187 | purpose: "oauth_access_token", |
| 191 | 188 | value: tokens.access_token, |
| 192 | 189 | name: `${currentArtifact.sourceId} MCP Access Token`, |
| 193 | 190 | }); |
| 194 | 191 | const refreshTokenRef = tokens.refresh_token |
| 195 | − ? yield* storeSecretMaterial({ | |
| 192 | + ? yield* input.storeSecretMaterial({ | |
| 196 | 193 | purpose: "oauth_refresh_token", |
| 197 | 194 | value: tokens.refresh_token, |
| 198 | 195 | name: `${currentArtifact.sourceId} MCP Refresh Token`, |
| @@ -212,7 +209,8 @@ export const createPersistedMcpAuthProvider = (input: { · 13 unmodified lines | ||
| 212 | 209 | }; |
| 213 | 210 | |
| 214 | 211 | yield* persistConfig(nextConfig); |
| 215 | − yield* cleanupReplacedSecretRefs(input.rows, { | |
| 212 | + yield* cleanupReplacedSecretRefs({ | |
| 213 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 216 | 214 | previousAccessToken: previousConfig.accessToken, |
| 217 | 215 | nextAccessToken: accessTokenRef, |
| 218 | 216 | previousRefreshToken: previousConfig.refreshToken, |
| @@ -9,7 +9,9 @@ import { · 8 unmodified lines | ||
| 9 | 9 | import * as Effect from "effect/Effect"; |
| 10 | 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 | 15 | import type { ControlPlaneStoreShape } from "../store"; |
| 14 | 16 | |
| 15 | 17 | const providerGrantRefFromArtifact = ( |
| @@ -81,11 +83,8 @@ export const markProviderGrantOrphanedIfUnused = (rows: ControlPlaneStoreShape, · 65 unmodified lines | ||
| 81 | 83 | |
| 82 | 84 | export const removeProviderAuthGrantSecret = (rows: ControlPlaneStoreShape, input: { |
| 83 | 85 | grant: ProviderAuthGrant; |
| 84 | −}): Effect.Effect<void, Error, never> => | |
| 86 | +}, deleteSecretMaterial: DeleteSecretMaterial): Effect.Effect<void, Error, never> => | |
| 85 | 87 | Effect.gen(function* () { |
| 86 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | |
| 87 | − rows, | |
| 88 | − }); | |
| 89 | 88 | yield* deleteSecretMaterial(input.grant.refreshToken).pipe( |
| 90 | 89 | Effect.either, |
| 91 | 90 | Effect.ignore, |
| @@ -17,10 +17,16 @@ import { · 16 unmodified lines | ||
| 17 | 17 | } from "./auth-artifacts"; |
| 18 | 18 | import { resolveAuthArtifactMaterialWithLeases } from "./auth-leases"; |
| 19 | 19 | import type { |
| 20 | + DeleteSecretMaterial, | |
| 20 | 21 | ResolveSecretMaterial, |
| 21 | 22 | SecretMaterialResolveContext, |
| 22 | −} from "../local/secret-material-providers"; | |
| 23 | −import { SecretMaterialResolverService } from "../local/secret-material-providers"; | |
| 23 | + StoreSecretMaterial, | |
| 24 | +} from "../workspace/secret-material-providers"; | |
| 25 | +import { | |
| 26 | + SecretMaterialDeleterService, | |
| 27 | + SecretMaterialResolverService, | |
| 28 | + SecretMaterialStorerService, | |
| 29 | +} from "../workspace/secret-material-providers"; | |
| 24 | 30 | import { runtimeEffectError } from "../effect-errors"; |
| 25 | 31 | |
| 26 | 32 | const authForSlot = (input: { |
| @@ -61,6 +67,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 34 unmodified lines | ||
| 61 | 67 | actorAccountId?: AccountId | null; |
| 62 | 68 | rows?: ControlPlaneStoreShape; |
| 63 | 69 | resolveSecretMaterial: ResolveSecretMaterial; |
| 70 | + storeSecretMaterial?: StoreSecretMaterial; | |
| 71 | + deleteSecretMaterial?: DeleteSecretMaterial; | |
| 64 | 72 | context?: SecretMaterialResolveContext; |
| 65 | 73 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => |
| 66 | 74 | Effect.gen(function* () { |
| @@ -85,6 +93,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 18 unmodified lines | ||
| 85 | 93 | rows: input.rows, |
| 86 | 94 | artifact: artifactOption.value, |
| 87 | 95 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 96 | + storeSecretMaterial: input.storeSecretMaterial ?? (() => Effect.dieMessage("storeSecretMaterial unavailable")), | |
| 97 | + deleteSecretMaterial: input.deleteSecretMaterial ?? (() => Effect.dieMessage("deleteSecretMaterial unavailable")), | |
| 88 | 98 | context: input.context, |
| 89 | 99 | }); |
| 90 | 100 | } |
| @@ -106,6 +116,8 @@ export const resolveSourceAuthMaterialWithDeps = (input: { · 15 unmodified lines | ||
| 106 | 116 | rows: input.rows, |
| 107 | 117 | artifact, |
| 108 | 118 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 119 | + storeSecretMaterial: input.storeSecretMaterial ?? (() => Effect.dieMessage("storeSecretMaterial unavailable")), | |
| 120 | + deleteSecretMaterial: input.deleteSecretMaterial ?? (() => Effect.dieMessage("deleteSecretMaterial unavailable")), | |
| 109 | 121 | context: input.context, |
| 110 | 122 | }); |
| 111 | 123 | } |
| @@ -138,6 +150,8 @@ export const RuntimeSourceAuthMaterialLive = Layer.effect( · 26 unmodified lines | ||
| 138 | 150 | Effect.gen(function* () { |
| 139 | 151 | const rows = yield* ControlPlaneStore; |
| 140 | 152 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 153 | + const storeSecretMaterial = yield* SecretMaterialStorerService; | |
| 154 | + const deleteSecretMaterial = yield* SecretMaterialDeleterService; | |
| 141 | 155 | |
| 142 | 156 | return RuntimeSourceAuthMaterialService.of({ |
| 143 | 157 | resolve: (input) => |
| @@ -145,6 +159,8 @@ export const RuntimeSourceAuthMaterialLive = Layer.effect( · 1 unmodified line | ||
| 145 | 159 | ...input, |
| 146 | 160 | rows, |
| 147 | 161 | resolveSecretMaterial, |
| 162 | + storeSecretMaterial, | |
| 163 | + deleteSecretMaterial, | |
| 148 | 164 | }), |
| 149 | 165 | }); |
| 150 | 166 | }), |
| @@ -5,8 +5,8 @@ import type { · 4 unmodified lines | ||
| 5 | 5 | } from "#schema"; |
| 6 | 6 | import * as Effect from "effect/Effect"; |
| 7 | 7 | |
| 8 | −import { RuntimeLocalWorkspaceService } from "../../local/runtime-context"; | |
| 9 | −import { SourceArtifactStore } from "../../local/storage"; | |
| 8 | +import { RuntimeLocalWorkspaceService } from "../../workspace/runtime-context"; | |
| 9 | +import { SourceArtifactStore } from "../../workspace/storage"; | |
| 10 | 10 | import { getSourceAdapterForSource } from "../../sources/source-adapters"; |
| 11 | 11 | import { RuntimeSourceStoreService } from "../../sources/source-store"; |
| 12 | 12 | import { RuntimeSourceCatalogSyncService } from "./sync"; |
| @@ -42,7 +42,6 @@ export const reconcileMissingSourceCatalogArtifacts = (input: { · 29 unmodified lines | ||
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | const artifact = yield* sourceArtifactStore.read({ |
| 45 | − context: runtimeLocalWorkspace.context, | |
| 46 | 45 | sourceId: source.id, |
| 47 | 46 | }); |
| 48 | 47 | if (artifact !== null) { |
| @@ -27,7 +27,7 @@ import type { · 26 unmodified lines | ||
| 27 | 27 | Executable, |
| 28 | 28 | ShapeSymbol, |
| 29 | 29 | } from "@executor/ir/model"; |
| 30 | −import { LocalSourceArtifactMissingError } from "../../local/errors"; | |
| 30 | +import { LocalSourceArtifactMissingError } from "../../workspace-errors"; | |
| 31 | 31 | import { |
| 32 | 32 | createCatalogTypeProjector, |
| 33 | 33 | documentationComment, |
| @@ -40,12 +40,12 @@ import { formatWithPrettier } from "../prettier-format"; · 6 unmodified lines | ||
| 40 | 40 | import { |
| 41 | 41 | RuntimeLocalWorkspaceService, |
| 42 | 42 | type RuntimeLocalWorkspaceState, |
| 43 | −} from "../../local/runtime-context"; | |
| 44 | −import type { LocalSourceArtifact } from "../../local/source-artifacts"; | |
| 43 | +} from "../../workspace/runtime-context"; | |
| 44 | +import type { LocalSourceArtifact } from "../../source-artifacts"; | |
| 45 | 45 | import { |
| 46 | 46 | SourceArtifactStore, |
| 47 | 47 | type SourceArtifactStoreShape, |
| 48 | −} from "../../local/storage"; | |
| 48 | +} from "../../workspace/storage"; | |
| 49 | 49 | import { |
| 50 | 50 | RuntimeSourceStoreService, |
| 51 | 51 | type RuntimeSourceStore, |
| @@ -704,17 +704,16 @@ type SourceCatalogRuntimeServices = · 652 unmodified lines | ||
| 704 | 704 | const ensureRuntimeCatalogWorkspace = ( |
| 705 | 705 | deps: RuntimeSourceCatalogStoreDeps, |
| 706 | 706 | workspaceId: WorkspaceId, |
| 707 | −) => { | |
| 707 | +) => | |
| 708 | + Effect.gen(function* () { | |
| 708 | 709 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { |
| 709 | − return Effect.fail( | |
| 710 | − runtimeEffectError("catalog/source/runtime", | |
| 710 | + return yield* Effect.fail( | |
| 711 | + runtimeEffectError("catalog/source/runtime", | |
| 711 | 712 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, |
| 712 | 713 | ), |
| 713 | 714 | ); |
| 714 | 715 | } |
| 715 | − | |
| 716 | − return Effect.succeed(deps.runtimeLocalWorkspace.context); | |
| 717 | −}; | |
| 716 | + }); | |
| 718 | 717 | |
| 719 | 718 | const buildSnapshotFromArtifact = (input: { |
| 720 | 719 | source: Source; |
| @@ -728,10 +727,7 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 7 unmodified lines | ||
| 728 | 727 | actorAccountId?: AccountId | null; |
| 729 | 728 | }): Effect.Effect<readonly LoadedSourceCatalog[], Error, never> => |
| 730 | 729 | Effect.gen(function* () { |
| 731 | − const workspaceContext = yield* ensureRuntimeCatalogWorkspace( | |
| 732 | − deps, | |
| 733 | − input.workspaceId, | |
| 734 | − ); | |
| 730 | + yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId); | |
| 735 | 731 | const sources = yield* deps.sourceStore.loadSourcesInWorkspace( |
| 736 | 732 | input.workspaceId, |
| 737 | 733 | { |
| @@ -742,7 +738,6 @@ const loadWorkspaceSourceCatalogsWithDeps = (deps: RuntimeSourceCatalogStoreDeps · 4 unmodified lines | ||
| 742 | 738 | const localCatalogs = yield* Effect.forEach(sources, (source) => |
| 743 | 739 | Effect.gen(function* () { |
| 744 | 740 | const artifact = yield* deps.sourceArtifactStore.read({ |
| 745 | − context: workspaceContext, | |
| 746 | 741 | sourceId: source.id, |
| 747 | 742 | }); |
| 748 | 743 | if (artifact === null) { |
| @@ -783,17 +778,13 @@ const loadSourceWithCatalogWithDeps = (deps: RuntimeSourceCatalogStoreDeps, inpu · 34 unmodified lines | ||
| 783 | 778 | actorAccountId?: AccountId | null; |
| 784 | 779 | }): Effect.Effect<LoadedSourceCatalog, Error | LocalSourceArtifactMissingError, never> => |
| 785 | 780 | Effect.gen(function* () { |
| 786 | − const workspaceContext = yield* ensureRuntimeCatalogWorkspace( | |
| 787 | − deps, | |
| 788 | − input.workspaceId, | |
| 789 | − ); | |
| 781 | + yield* ensureRuntimeCatalogWorkspace(deps, input.workspaceId); | |
| 790 | 782 | const source = yield* deps.sourceStore.loadSourceById({ |
| 791 | 783 | workspaceId: input.workspaceId, |
| 792 | 784 | sourceId: input.sourceId, |
| 793 | 785 | actorAccountId: input.actorAccountId, |
| 794 | 786 | }); |
| 795 | 787 | const artifact = yield* deps.sourceArtifactStore.read({ |
| 796 | − context: workspaceContext, | |
| 797 | 788 | sourceId: source.id, |
| 798 | 789 | }); |
| 799 | 790 | if (artifact === null) { |
| @@ -11,16 +11,16 @@ import * as Layer from "effect/Layer"; · 10 unmodified lines | ||
| 11 | 11 | import { |
| 12 | 12 | RuntimeLocalWorkspaceService, |
| 13 | 13 | type RuntimeLocalWorkspaceState, |
| 14 | −} from "../../local/runtime-context"; | |
| 14 | +} from "../../workspace/runtime-context"; | |
| 15 | 15 | import { |
| 16 | 16 | SourceArtifactStore, |
| 17 | 17 | type SourceArtifactStoreShape, |
| 18 | 18 | WorkspaceStateStore, |
| 19 | 19 | type WorkspaceStateStoreShape, |
| 20 | −} from "../../local/storage"; | |
| 20 | +} from "../../workspace/storage"; | |
| 21 | 21 | import { |
| 22 | 22 | type LocalWorkspaceState, |
| 23 | −} from "../../local/workspace-state"; | |
| 23 | +} from "../../workspace-state"; | |
| 24 | 24 | import { |
| 25 | 25 | RuntimeSourceAuthMaterialService, |
| 26 | 26 | } from "../../auth/source-auth-material"; |
| @@ -30,10 +30,11 @@ import { · 3 unmodified lines | ||
| 30 | 30 | import { |
| 31 | 31 | catalogSyncResultFromMcpManifest, |
| 32 | 32 | } from "@executor/source-mcp"; |
| 33 | −import { SecretMaterialResolverService } from "../../local/secret-material-providers"; | |
| 33 | +import { SecretMaterialResolverService } from "../../workspace/secret-material-providers"; | |
| 34 | 34 | import { snapshotFromSourceCatalogSyncResult } from "@executor/source-core"; |
| 35 | 35 | import { |
| 36 | − refreshSourceTypeDeclarationInBackground, | |
| 36 | + SourceTypeDeclarationsRefresherService, | |
| 37 | + type SourceTypeDeclarationsRefresherShape, | |
| 37 | 38 | } from "./type-declarations"; |
| 38 | 39 | import { runtimeEffectError } from "../../effect-errors"; |
| 39 | 40 | |
| @@ -46,6 +47,7 @@ type RuntimeSourceCatalogSyncDeps = { · 6 unmodified lines | ||
| 46 | 47 | runtimeLocalWorkspace: RuntimeLocalWorkspaceState; |
| 47 | 48 | workspaceStateStore: WorkspaceStateStoreShape; |
| 48 | 49 | sourceArtifactStore: SourceArtifactStoreShape; |
| 50 | + sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape; | |
| 49 | 51 | resolveSecretMaterial: Effect.Effect.Success<typeof SecretMaterialResolverService>; |
| 50 | 52 | sourceAuthMaterialService: Effect.Effect.Success<typeof RuntimeSourceAuthMaterialService>; |
| 51 | 53 | }; |
| @@ -54,6 +56,7 @@ type SourceCatalogSyncServices = · 2 unmodified lines | ||
| 54 | 56 | | RuntimeLocalWorkspaceService |
| 55 | 57 | | WorkspaceStateStore |
| 56 | 58 | | SourceArtifactStore |
| 59 | + | SourceTypeDeclarationsRefresherService | |
| 57 | 60 | | RuntimeSourceAuthMaterialService |
| 58 | 61 | | SecretMaterialResolverService; |
| 59 | 62 | |
| @@ -75,17 +78,16 @@ export class RuntimeSourceCatalogSyncService extends Context.Tag( · 15 unmodified lines | ||
| 75 | 78 | const ensureRuntimeCatalogSyncWorkspace = ( |
| 76 | 79 | deps: RuntimeSourceCatalogSyncDeps, |
| 77 | 80 | workspaceId: Source["workspaceId"], |
| 78 | −) => { | |
| 81 | +) => | |
| 82 | + Effect.gen(function* () { | |
| 79 | 83 | if (deps.runtimeLocalWorkspace.installation.workspaceId !== workspaceId) { |
| 80 | − return Effect.fail( | |
| 84 | + return yield* Effect.fail( | |
| 81 | 85 | runtimeEffectError("catalog/source/sync", |
| 82 | 86 | `Runtime local workspace mismatch: expected ${workspaceId}, got ${deps.runtimeLocalWorkspace.installation.workspaceId}`, |
| 83 | 87 | ), |
| 84 | 88 | ); |
| 85 | 89 | } |
| 86 | − | |
| 87 | − return Effect.succeed(deps.runtimeLocalWorkspace.context); | |
| 88 | −}; | |
| 90 | + }); | |
| 89 | 91 | |
| 90 | 92 | const syncSourceCatalogWithDeps = ( |
| 91 | 93 | deps: RuntimeSourceCatalogSyncDeps, |
| @@ -95,13 +97,10 @@ const syncSourceCatalogWithDeps = ( · 3 unmodified lines | ||
| 95 | 97 | }, |
| 96 | 98 | ): Effect.Effect<void, Error, never> => |
| 97 | 99 | Effect.gen(function* () { |
| 98 | − const workspaceContext = yield* ensureRuntimeCatalogSyncWorkspace( | |
| 99 | − deps, | |
| 100 | − input.source.workspaceId, | |
| 101 | − ); | |
| 100 | + yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId); | |
| 102 | 101 | |
| 103 | 102 | if (!shouldIndexSource(input.source)) { |
| 104 | − const state = yield* deps.workspaceStateStore.load(workspaceContext); | |
| 103 | + const state = yield* deps.workspaceStateStore.load(); | |
| 105 | 104 | const existingSourceState = state.sources[input.source.id]; |
| 106 | 105 | const nextState: LocalWorkspaceState = { |
| 107 | 106 | ...state, |
| @@ -117,15 +116,11 @@ const syncSourceCatalogWithDeps = ( · 9 unmodified lines | ||
| 117 | 116 | }, |
| 118 | 117 | }; |
| 119 | 118 | yield* deps.workspaceStateStore.write({ |
| 120 | − context: workspaceContext, | |
| 121 | 119 | state: nextState, |
| 122 | 120 | }); |
| 123 | − yield* Effect.sync(() => { | |
| 124 | − refreshSourceTypeDeclarationInBackground({ | |
| 125 | − context: workspaceContext, | |
| 126 | − source: input.source, | |
| 127 | − snapshot: null, | |
| 128 | − }); | |
| 121 | + yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ | |
| 122 | + source: input.source, | |
| 123 | + snapshot: null, | |
| 129 | 124 | }); |
| 130 | 125 | return; |
| 131 | 126 | } |
| @@ -143,7 +138,6 @@ const syncSourceCatalogWithDeps = ( · 11 unmodified lines | ||
| 143 | 138 | }); |
| 144 | 139 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); |
| 145 | 140 | yield* deps.sourceArtifactStore.write({ |
| 146 | − context: workspaceContext, | |
| 147 | 141 | sourceId: input.source.id, |
| 148 | 142 | artifact: deps.sourceArtifactStore.build({ |
| 149 | 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 | 149 | const existingSourceState = state.sources[input.source.id]; |
| 156 | 150 | const nextState: LocalWorkspaceState = { |
| 157 | 151 | ...state, |
| @@ -167,16 +161,12 @@ const syncSourceCatalogWithDeps = ( · 9 unmodified lines | ||
| 167 | 161 | }, |
| 168 | 162 | }; |
| 169 | 163 | yield* deps.workspaceStateStore.write({ |
| 170 | − context: workspaceContext, | |
| 171 | 164 | state: nextState, |
| 172 | 165 | }); |
| 173 | 166 | |
| 174 | − yield* Effect.sync(() => { | |
| 175 | − refreshSourceTypeDeclarationInBackground({ | |
| 176 | − context: workspaceContext, | |
| 177 | − source: input.source, | |
| 178 | − snapshot, | |
| 179 | − }); | |
| 167 | + yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ | |
| 168 | + source: input.source, | |
| 169 | + snapshot, | |
| 180 | 170 | }); |
| 181 | 171 | }).pipe( |
| 182 | 172 | Effect.withSpan("source.catalog.sync", { |
| @@ -197,10 +187,7 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 14 unmodified lines | ||
| 197 | 187 | }, |
| 198 | 188 | ): Effect.Effect<void, Error, never> => |
| 199 | 189 | Effect.gen(function* () { |
| 200 | − const workspaceContext = yield* ensureRuntimeCatalogSyncWorkspace( | |
| 201 | − deps, | |
| 202 | − input.source.workspaceId, | |
| 203 | − ); | |
| 190 | + yield* ensureRuntimeCatalogSyncWorkspace(deps, input.source.workspaceId); | |
| 204 | 191 | const syncResult = catalogSyncResultFromMcpManifest({ |
| 205 | 192 | source: input.source, |
| 206 | 193 | endpoint: input.source.endpoint, |
| @@ -209,7 +196,6 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 2 unmodified lines | ||
| 209 | 196 | const snapshot = snapshotFromSourceCatalogSyncResult(syncResult); |
| 210 | 197 | |
| 211 | 198 | yield* deps.sourceArtifactStore.write({ |
| 212 | − context: workspaceContext, | |
| 213 | 199 | sourceId: input.source.id, |
| 214 | 200 | artifact: deps.sourceArtifactStore.build({ |
| 215 | 201 | source: input.source, |
| @@ -217,12 +203,9 @@ const persistMcpCatalogSnapshotFromManifestWithDeps = ( · 1 unmodified line | ||
| 217 | 203 | }), |
| 218 | 204 | }); |
| 219 | 205 | |
| 220 | − yield* Effect.sync(() => { | |
| 221 | − refreshSourceTypeDeclarationInBackground({ | |
| 222 | − context: workspaceContext, | |
| 223 | − source: input.source, | |
| 224 | − snapshot, | |
| 225 | − }); | |
| 206 | + yield* deps.sourceTypeDeclarationsRefresher.refreshSourceInBackground({ | |
| 207 | + source: input.source, | |
| 208 | + snapshot, | |
| 226 | 209 | }); |
| 227 | 210 | }); |
| 228 | 211 | |
| @@ -234,6 +217,8 @@ export const syncSourceCatalog = (input: { · 5 unmodified lines | ||
| 234 | 217 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 235 | 218 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 236 | 219 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 220 | + const sourceTypeDeclarationsRefresher = | |
| 221 | + yield* SourceTypeDeclarationsRefresherService; | |
| 237 | 222 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 238 | 223 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 239 | 224 | |
| @@ -242,6 +227,7 @@ export const syncSourceCatalog = (input: { · 2 unmodified lines | ||
| 242 | 227 | runtimeLocalWorkspace, |
| 243 | 228 | workspaceStateStore, |
| 244 | 229 | sourceArtifactStore, |
| 230 | + sourceTypeDeclarationsRefresher, | |
| 245 | 231 | resolveSecretMaterial, |
| 246 | 232 | sourceAuthMaterialService, |
| 247 | 233 | }, |
| @@ -260,6 +246,8 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 12 unmodified lines | ||
| 260 | 246 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 261 | 247 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 262 | 248 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 249 | + const sourceTypeDeclarationsRefresher = | |
| 250 | + yield* SourceTypeDeclarationsRefresherService; | |
| 263 | 251 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 264 | 252 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 265 | 253 | |
| @@ -268,6 +256,7 @@ export const persistMcpCatalogSnapshotFromManifest = (input: { · 2 unmodified lines | ||
| 268 | 256 | runtimeLocalWorkspace, |
| 269 | 257 | workspaceStateStore, |
| 270 | 258 | sourceArtifactStore, |
| 259 | + sourceTypeDeclarationsRefresher, | |
| 271 | 260 | resolveSecretMaterial, |
| 272 | 261 | sourceAuthMaterialService, |
| 273 | 262 | }, |
| @@ -281,6 +270,8 @@ export const RuntimeSourceCatalogSyncLive = Layer.effect( · 7 unmodified lines | ||
| 281 | 270 | const runtimeLocalWorkspace = yield* RuntimeLocalWorkspaceService; |
| 282 | 271 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 283 | 272 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 273 | + const sourceTypeDeclarationsRefresher = | |
| 274 | + yield* SourceTypeDeclarationsRefresherService; | |
| 284 | 275 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 285 | 276 | const sourceAuthMaterialService = yield* RuntimeSourceAuthMaterialService; |
| 286 | 277 | |
| @@ -288,6 +279,7 @@ export const RuntimeSourceCatalogSyncLive = Layer.effect( · 1 unmodified line | ||
| 288 | 279 | runtimeLocalWorkspace, |
| 289 | 280 | workspaceStateStore, |
| 290 | 281 | sourceArtifactStore, |
| 282 | + sourceTypeDeclarationsRefresher, | |
| 291 | 283 | resolveSecretMaterial, |
| 292 | 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 | 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 | 2 | import type { CatalogSnapshotV1 } from "@executor/ir/model"; |
| 11 | −import { | |
| 12 | − createCatalogTypeProjector, | |
| 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"; | |
| 3 | +import * as Context from "effect/Context"; | |
| 4 | +import * as Effect from "effect/Effect"; | |
| 23 | 5 | |
| 24 | −type SourceDeclarationEntry = { | |
| 6 | +export type BoundSourceDeclarationEntry = { | |
| 25 | 7 | source: Source; |
| 26 | 8 | snapshot: CatalogSnapshotV1; |
| 27 | 9 | }; |
| 28 | 10 | |
| 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 | − }); | |
| 11 | +export type SourceTypeDeclarationsRefresherShape = { | |
| 12 | + refreshWorkspaceInBackground: (input: { | |
| 13 | + entries: readonly BoundSourceDeclarationEntry[]; | |
| 14 | + }) => Effect.Effect<void, never, never>; | |
| 15 | + refreshSourceInBackground: (input: { | |
| 16 | + source: Source; | |
| 17 | + snapshot: CatalogSnapshotV1 | null; | |
| 18 | + }) => Effect.Effect<void, never, never>; | |
| 396 | 19 | }; |
| 397 | 20 | |
| 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 | −}; | |
| 21 | +export class SourceTypeDeclarationsRefresherService extends Context.Tag( | |
| 22 | + "#runtime/SourceTypeDeclarationsRefresherService", | |
| 23 | +)<SourceTypeDeclarationsRefresherService, SourceTypeDeclarationsRefresherShape>() {} | |
| @@ -31,28 +31,26 @@ import { · 30 unmodified lines | ||
| 31 | 31 | provideControlPlaneRuntime, |
| 32 | 32 | RuntimeExecutionResolverService, |
| 33 | 33 | } from "./index"; |
| 34 | −import { createLocalControlPlaneRuntime as createControlPlaneRuntime } from "../../../sdk-file/src/index"; | |
| 35 | 34 | import { createSourceFromPayload } from "./sources/source-definitions"; |
| 36 | 35 | import { decodeSourceCredentialSelectionContent } from "./sources/source-credential-interactions"; |
| 37 | 36 | import { persistSource } from "./sources/source-store"; |
| 38 | 37 | import { withControlPlaneClient } from "./execution/test-http-client"; |
| 39 | 38 | import { runtimeEffectError } from "./effect-errors"; |
| 40 | 39 | import { |
| 40 | + buildLocalSourceArtifact, | |
| 41 | + createLocalControlPlaneRuntime as createControlPlaneRuntime, | |
| 42 | + deriveLocalInstallation, | |
| 43 | + readLocalSourceArtifact, | |
| 41 | 44 | resolveLocalWorkspaceContext, |
| 45 | + writeLocalControlPlaneState, | |
| 46 | + writeLocalSourceArtifact, | |
| 42 | 47 | writeProjectLocalExecutorConfig, |
| 43 | −} from "./local/config"; | |
| 44 | −import { writeLocalControlPlaneState } from "./local/control-plane-store"; | |
| 45 | −import { deriveLocalInstallation } from "./local/installation"; | |
| 48 | + writeLocalWorkspaceState, | |
| 49 | +} from "../../../sdk-file/src/index"; | |
| 46 | 50 | import { |
| 47 | 51 | decodeSourceCredentialInteraction, |
| 48 | 52 | submitSourceCredentialInteraction, |
| 49 | −} from "./local/operations"; | |
| 50 | −import { | |
| 51 | − buildLocalSourceArtifact, | |
| 52 | − readLocalSourceArtifact, | |
| 53 | − writeLocalSourceArtifact, | |
| 54 | −} from "./local/source-artifacts"; | |
| 55 | −import { writeLocalWorkspaceState } from "./local/workspace-state"; | |
| 53 | +} from "../local/operations"; | |
| 56 | 54 | |
| 57 | 55 | const makeRuntime = Effect.gen(function* () { |
| 58 | 56 | const fs = yield* FileSystem.FileSystem; |
| @@ -38,7 +38,7 @@ import { · 37 unmodified lines | ||
| 38 | 38 | import { |
| 39 | 39 | getRuntimeLocalWorkspaceOption, |
| 40 | 40 | provideOptionalRuntimeLocalWorkspace, |
| 41 | −} from "../local/runtime-context"; | |
| 41 | +} from "../workspace/runtime-context"; | |
| 42 | 42 | import { |
| 43 | 43 | asOperationErrors, |
| 44 | 44 | operationErrors, |
| @@ -5,12 +5,11 @@ import * as Effect from "effect/Effect"; · 4 unmodified lines | ||
| 5 | 5 | import * as Schema from "effect/Schema"; |
| 6 | 6 | |
| 7 | 7 | import type { LoadedSourceCatalogToolIndexEntry } from "../../catalog/source/runtime"; |
| 8 | −import type { SecretMaterialResolveContext } from "../../local/secret-material-providers"; | |
| 9 | −import type { WorkspaceStorageServices } from "../../local/storage"; | |
| 8 | +import type { SecretMaterialResolveContext } from "../../workspace/secret-material-providers"; | |
| 9 | +import type { WorkspaceStorageServices } from "../../workspace/storage"; | |
| 10 | 10 | import { invocationDescriptorFromTool } from "../ir-execution"; |
| 11 | 11 | import { evaluateInvocationPolicy } from "../../policy/invocation-policy-engine"; |
| 12 | −import { loadRuntimeLocalWorkspacePolicies } from "../../policy/policies-operations"; | |
| 13 | −import { runtimeEffectError } from "../../effect-errors"; | |
| 12 | +import { loadRuntimeLocalWorkspacePolicies } from "../../../policies/operations"; | |
| 14 | 13 | |
| 15 | 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 | 67 | type WorkspaceToolElicitation = Parameters< |
| 66 | 68 | typeof makeToolInvokerFromTools |
| 67 | 69 | >[0]["onElicitation"]; |
| @@ -99,13 +101,13 @@ export const authorizePersistedToolInvocation = (input: { · 31 unmodified lines | ||
| 99 | 101 | } |
| 100 | 102 | |
| 101 | 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 | 107 | if (!input.onElicitation) { |
| 106 | − return yield* runtimeEffectError("execution/workspace/authorization", | |
| 107 | − `Approval required for ${descriptor.toolPath}, but no elicitation-capable host is available`, | |
| 108 | − ); | |
| 108 | + return yield* failAuthorization( | |
| 109 | + `Approval required for ${descriptor.toolPath}, but no elicitation-capable host is available`, | |
| 110 | + ); | |
| 109 | 111 | } |
| 110 | 112 | |
| 111 | 113 | const interactionId = |
| @@ -144,8 +146,8 @@ export const authorizePersistedToolInvocation = (input: { · 32 unmodified lines | ||
| 144 | 146 | ); |
| 145 | 147 | |
| 146 | 148 | if (response.action !== "accept") { |
| 147 | − return yield* runtimeEffectError("execution/workspace/authorization", | |
| 148 | − `Tool invocation not approved for ${descriptor.toolPath}`, | |
| 149 | − ); | |
| 149 | + return yield* failAuthorization( | |
| 150 | + `Tool invocation not approved for ${descriptor.toolPath}`, | |
| 151 | + ); | |
| 150 | 152 | } |
| 151 | 153 | }); |
| @@ -19,12 +19,18 @@ import { RuntimeSourceAuthServiceTag } from "../../sources/source-auth-service"; · 18 unmodified lines | ||
| 19 | 19 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; |
| 20 | 20 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; |
| 21 | 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 | 29 | import { |
| 24 | 30 | LocalToolRuntimeLoaderService, |
| 25 | 31 | type LocalToolRuntimeLoaderShape, |
| 26 | 32 | type LocalToolRuntime, |
| 27 | −} from "../../local/tools"; | |
| 33 | +} from "../../local-tool-runtime"; | |
| 28 | 34 | import { |
| 29 | 35 | InstallationStore, |
| 30 | 36 | type InstallationStoreShape, |
| @@ -34,7 +40,7 @@ import { · 3 unmodified lines | ||
| 34 | 40 | type WorkspaceConfigStoreShape, |
| 35 | 41 | WorkspaceStateStore, |
| 36 | 42 | type WorkspaceStateStoreShape, |
| 37 | −} from "../../local/storage"; | |
| 43 | +} from "../../workspace/storage"; | |
| 38 | 44 | import { ControlPlaneStore } from "../../store"; |
| 39 | 45 | import { RuntimeSourceStoreService } from "../../sources/source-store"; |
| 40 | 46 | import type { CreateWorkspaceInternalToolMap } from "./tool-invoker"; |
| @@ -68,6 +74,10 @@ export const createWorkspaceExecutionEnvironmentResolver = · 27 unmodified lines | ||
| 68 | 74 | >; |
| 69 | 75 | localToolRuntimeLoader: LocalToolRuntimeLoaderShape; |
| 70 | 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 | 81 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 72 | 82 | workspaceStateStore: WorkspaceStateStoreShape; |
| 73 | 83 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -79,15 +89,11 @@ export const createWorkspaceExecutionEnvironmentResolver = · 5 unmodified lines | ||
| 79 | 89 | const loadedConfig = |
| 80 | 90 | runtimeLocalWorkspace === null |
| 81 | 91 | ? null |
| 82 | − : yield* input.workspaceConfigStore.load( | |
| 83 | − runtimeLocalWorkspace.context, | |
| 84 | − ); | |
| 92 | + : yield* input.workspaceConfigStore.load(); | |
| 85 | 93 | const localToolRuntime = |
| 86 | 94 | runtimeLocalWorkspace === null |
| 87 | 95 | ? createEmptyLocalToolRuntime() |
| 88 | − : yield* input.localToolRuntimeLoader.load( | |
| 89 | − runtimeLocalWorkspace.context, | |
| 90 | − ); | |
| 96 | + : yield* input.localToolRuntimeLoader.load(); | |
| 91 | 97 | const { catalog, toolInvoker } = createWorkspaceToolInvoker({ |
| 92 | 98 | workspaceId, |
| 93 | 99 | accountId, |
| @@ -96,6 +102,10 @@ export const createWorkspaceExecutionEnvironmentResolver = · 2 unmodified lines | ||
| 96 | 102 | sourceCatalogSyncService: input.sourceCatalogSyncService, |
| 97 | 103 | sourceCatalogStore: input.sourceCatalogStore, |
| 98 | 104 | installationStore: input.installationStore, |
| 105 | + instanceConfigResolver: input.instanceConfigResolver, | |
| 106 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 107 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 108 | + updateSecretMaterial: input.updateSecretMaterial, | |
| 99 | 109 | workspaceConfigStore: input.workspaceConfigStore, |
| 100 | 110 | workspaceStateStore: input.workspaceStateStore, |
| 101 | 111 | sourceArtifactStore: input.sourceArtifactStore, |
| @@ -146,6 +156,10 @@ export const RuntimeExecutionResolverLive = ( · 44 unmodified lines | ||
| 146 | 156 | const sourceCatalogStore = yield* RuntimeSourceCatalogStoreService; |
| 147 | 157 | const localToolRuntimeLoader = yield* LocalToolRuntimeLoaderService; |
| 148 | 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 | 163 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 150 | 164 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 151 | 165 | const sourceArtifactStore = yield* SourceArtifactStore; |
| @@ -159,6 +173,10 @@ export const RuntimeExecutionResolverLive = ( · 7 unmodified lines | ||
| 159 | 173 | sourceCatalogStore, |
| 160 | 174 | localToolRuntimeLoader, |
| 161 | 175 | installationStore, |
| 176 | + instanceConfigResolver, | |
| 177 | + storeSecretMaterial, | |
| 178 | + deleteSecretMaterial, | |
| 179 | + updateSecretMaterial, | |
| 162 | 180 | workspaceConfigStore, |
| 163 | 181 | workspaceStateStore, |
| 164 | 182 | sourceArtifactStore, |
| @@ -3,7 +3,7 @@ import * as Effect from "effect/Effect"; · 2 unmodified lines | ||
| 3 | 3 | import { |
| 4 | 4 | provideOptionalRuntimeLocalWorkspace, |
| 5 | 5 | type RuntimeLocalWorkspaceState, |
| 6 | −} from "../../local/runtime-context"; | |
| 6 | +} from "../../workspace/runtime-context"; | |
| 7 | 7 | |
| 8 | 8 | export const provideRuntimeLocalWorkspace = <A, E, R>( |
| 9 | 9 | effect: Effect.Effect<A, E, R>, |
| @@ -10,14 +10,14 @@ import { · 9 unmodified lines | ||
| 10 | 10 | type LoadedSourceCatalogToolIndexEntry, |
| 11 | 11 | catalogToolCatalogEntry, |
| 12 | 12 | } from "../../catalog/source/runtime"; |
| 13 | −import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context"; | |
| 13 | +import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context"; | |
| 14 | 14 | import { |
| 15 | 15 | makeWorkspaceStorageLayer, |
| 16 | 16 | type SourceArtifactStoreShape, |
| 17 | 17 | type WorkspaceConfigStoreShape, |
| 18 | 18 | type WorkspaceStateStoreShape, |
| 19 | 19 | type WorkspaceStorageServices, |
| 20 | −} from "../../local/storage"; | |
| 20 | +} from "../../workspace/storage"; | |
| 21 | 21 | import { provideRuntimeLocalWorkspace } from "./local"; |
| 22 | 22 | |
| 23 | 23 | const tokenize = (value: string): string[] => |
| @@ -13,15 +13,21 @@ import * as Effect from "effect/Effect"; · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | import { RuntimeSourceAuthMaterialService } from "../../auth/source-auth-material"; |
| 15 | 15 | import { RuntimeSourceCatalogStoreService } from "../../catalog/source/runtime"; |
| 16 | −import type { RuntimeLocalWorkspaceState } from "../../local/runtime-context"; | |
| 17 | −import { type LocalToolRuntime } from "../../local/tools"; | |
| 16 | +import type { RuntimeLocalWorkspaceState } from "../../workspace/runtime-context"; | |
| 17 | +import { type LocalToolRuntime } from "../../local-tool-runtime"; | |
| 18 | 18 | import { |
| 19 | 19 | type InstallationStoreShape, |
| 20 | 20 | makeWorkspaceStorageLayer, |
| 21 | 21 | type SourceArtifactStoreShape, |
| 22 | 22 | type WorkspaceConfigStoreShape, |
| 23 | 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 | 31 | import type { ControlPlaneStoreShape } from "../../store"; |
| 26 | 32 | import { RuntimeSourceAuthService } from "../../sources/source-auth-service"; |
| 27 | 33 | import { type RuntimeSourceStore } from "../../sources/source-store"; |
| @@ -49,6 +55,10 @@ export type WorkspaceInternalToolContext = { · 21 unmodified lines | ||
| 49 | 55 | >; |
| 50 | 56 | sourceAuthService: RuntimeSourceAuthService; |
| 51 | 57 | installationStore: InstallationStoreShape; |
| 58 | + instanceConfigResolver: ResolveInstanceConfig; | |
| 59 | + storeSecretMaterial: StoreSecretMaterial; | |
| 60 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 61 | + updateSecretMaterial: UpdateSecretMaterial; | |
| 52 | 62 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 53 | 63 | workspaceStateStore: WorkspaceStateStoreShape; |
| 54 | 64 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -71,6 +81,10 @@ export const createWorkspaceToolInvoker = (input: { · 16 unmodified lines | ||
| 71 | 81 | typeof RuntimeSourceCatalogStoreService |
| 72 | 82 | >; |
| 73 | 83 | installationStore: InstallationStoreShape; |
| 84 | + instanceConfigResolver: ResolveInstanceConfig; | |
| 85 | + storeSecretMaterial: StoreSecretMaterial; | |
| 86 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 87 | + updateSecretMaterial: UpdateSecretMaterial; | |
| 74 | 88 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 75 | 89 | workspaceStateStore: WorkspaceStateStoreShape; |
| 76 | 90 | sourceArtifactStore: SourceArtifactStoreShape; |
| @@ -115,6 +129,10 @@ export const createWorkspaceToolInvoker = (input: { · 38 unmodified lines | ||
| 115 | 129 | sourceCatalogSyncService: input.sourceCatalogSyncService, |
| 116 | 130 | sourceAuthService: input.sourceAuthService, |
| 117 | 131 | installationStore: input.installationStore, |
| 132 | + instanceConfigResolver: input.instanceConfigResolver, | |
| 133 | + storeSecretMaterial: input.storeSecretMaterial, | |
| 134 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 135 | + updateSecretMaterial: input.updateSecretMaterial, | |
| 118 | 136 | workspaceConfigStore: input.workspaceConfigStore, |
| 119 | 137 | workspaceStateStore: input.workspaceStateStore, |
| 120 | 138 | sourceArtifactStore: input.sourceArtifactStore, |
| @@ -13,16 +13,22 @@ import { RuntimeSourceAuthMaterialLive } from "./auth/source-auth-material"; · 12 unmodified lines | ||
| 13 | 13 | import { RuntimeSourceCatalogStoreLive } from "./catalog/source/runtime"; |
| 14 | 14 | import { reconcileMissingSourceCatalogArtifacts } from "./catalog/source/reconcile"; |
| 15 | 15 | import { RuntimeSourceCatalogSyncLive } from "./catalog/source/sync"; |
| 16 | +import { | |
| 17 | + SourceTypeDeclarationsRefresherService, | |
| 18 | + type SourceTypeDeclarationsRefresherShape, | |
| 19 | +} from "./catalog/source/type-declarations"; | |
| 16 | 20 | import { createLiveExecutionManager, LiveExecutionManagerService } from "./execution/live"; |
| 17 | 21 | import { RuntimeExecutionResolverLive } from "./execution/workspace/environment"; |
| 18 | 22 | import type { CreateWorkspaceInternalToolMap, WorkspaceInternalToolContext } from "./execution/workspace/tool-invoker"; |
| 19 | −import type { | |
| 20 | − LoadedLocalExecutorConfig, | |
| 21 | − ResolvedLocalWorkspaceContext, | |
| 22 | −} from "./local/config"; | |
| 23 | +import type { LoadedLocalExecutorConfig } from "./workspace-config"; | |
| 23 | 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 | 30 | import { |
| 31 | + LocalInstanceConfigService, | |
| 26 | 32 | type DeleteSecretMaterial, |
| 27 | 33 | type ResolveSecretMaterial, |
| 28 | 34 | SecretMaterialDeleterService, |
| @@ -31,17 +37,17 @@ import { · 2 unmodified lines | ||
| 31 | 37 | SecretMaterialUpdaterService, |
| 32 | 38 | type StoreSecretMaterial, |
| 33 | 39 | type UpdateSecretMaterial, |
| 34 | −} from "./local/secret-material-providers"; | |
| 35 | −import type { LocalSourceArtifact } from "./local/source-artifacts"; | |
| 40 | +} from "./workspace/secret-material-providers"; | |
| 41 | +import type { LocalSourceArtifact } from "./source-artifacts"; | |
| 36 | 42 | import { |
| 37 | 43 | type RuntimeLocalWorkspaceState, |
| 38 | 44 | RuntimeLocalWorkspaceLive, |
| 39 | −} from "./local/runtime-context"; | |
| 45 | +} from "./workspace/runtime-context"; | |
| 40 | 46 | import { |
| 41 | 47 | type LocalToolRuntime, |
| 42 | 48 | type LocalToolRuntimeLoaderShape, |
| 43 | 49 | LocalToolRuntimeLoaderService, |
| 44 | −} from "./local/tools"; | |
| 50 | +} from "./local-tool-runtime"; | |
| 45 | 51 | import { |
| 46 | 52 | InstallationStore, |
| 47 | 53 | makeLocalStorageLayer, |
| @@ -49,9 +55,9 @@ import { · 1 unmodified line | ||
| 49 | 55 | type SourceArtifactStoreShape, |
| 50 | 56 | type WorkspaceConfigStoreShape, |
| 51 | 57 | type WorkspaceStateStoreShape, |
| 52 | −} from "./local/storage"; | |
| 53 | −import type { LocalWorkspaceState } from "./local/workspace-state"; | |
| 54 | −import { synchronizeLocalWorkspaceState } from "./local/workspace-sync"; | |
| 58 | +} from "./workspace/storage"; | |
| 59 | +import type { LocalWorkspaceState } from "./workspace-state"; | |
| 60 | +import { synchronizeLocalWorkspaceState } from "./workspace/workspace-sync"; | |
| 55 | 61 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store"; |
| 56 | 62 | import { RuntimeSourceAuthServiceLive } from "./sources/source-auth-service"; |
| 57 | 63 | import { RuntimeSourceStoreLive } from "./sources/source-store"; |
| @@ -59,19 +65,10 @@ import { RuntimeSourceStoreLive } from "./sources/source-store"; · 1 unmodified line | ||
| 59 | 65 | export * from "./execution/state"; |
| 60 | 66 | export * from "./sources/executor-tools"; |
| 61 | 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 | 68 | export * from "./catalog/schema-type-signature"; |
| 71 | 69 | export * from "./catalog/source/runtime"; |
| 72 | 70 | export * from "./catalog/source/sync"; |
| 73 | 71 | export * from "./sources/source-auth-service"; |
| 74 | −export * from "./local/secret-material-providers"; | |
| 75 | 72 | export * from "./sources/source-credential-interactions"; |
| 76 | 73 | export * from "./sources/source-adapters/mcp"; |
| 77 | 74 | export * from "./sources/source-store"; |
| @@ -84,6 +81,20 @@ export type { · 6 unmodified lines | ||
| 84 | 81 | CreateWorkspaceInternalToolMap, |
| 85 | 82 | WorkspaceInternalToolContext, |
| 86 | 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 | 98 | export { |
| 88 | 99 | builtInSourceAdapters, |
| 89 | 100 | connectableSourceAdapters, |
| @@ -154,6 +165,9 @@ export type BoundLocalToolRuntimeLoader = { · 64 unmodified lines | ||
| 154 | 165 | load: () => ReturnType<LocalToolRuntimeLoaderShape["load"]>; |
| 155 | 166 | }; |
| 156 | 167 | |
| 168 | +export type BoundSourceTypeDeclarationsRefresher = | |
| 169 | + SourceTypeDeclarationsRefresherShape; | |
| 170 | + | |
| 157 | 171 | export type RuntimeSecretMaterialServices = { |
| 158 | 172 | resolve: ResolveSecretMaterial; |
| 159 | 173 | store: StoreSecretMaterial; |
| @@ -161,15 +175,26 @@ export type RuntimeSecretMaterialServices = { · 1 unmodified line | ||
| 161 | 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 | 187 | export type RuntimeControlPlaneServices = { |
| 165 | − workspaceContext: ResolvedLocalWorkspaceContext; | |
| 188 | + workspace: ExecutorWorkspaceDescriptor; | |
| 166 | 189 | installationStore: BoundInstallationStore; |
| 167 | 190 | workspaceConfigStore: BoundWorkspaceConfigStore; |
| 168 | 191 | workspaceStateStore: BoundWorkspaceStateStore; |
| 169 | 192 | sourceArtifactStore: BoundSourceArtifactStore; |
| 170 | 193 | localToolRuntimeLoader?: BoundLocalToolRuntimeLoader; |
| 194 | + sourceTypeDeclarationsRefresher?: BoundSourceTypeDeclarationsRefresher; | |
| 171 | 195 | secretMaterial: RuntimeSecretMaterialServices; |
| 172 | − persistence: LocalControlPlanePersistence; | |
| 196 | + instanceConfig: RuntimeInstanceConfigService; | |
| 197 | + persistence: RuntimePersistence; | |
| 173 | 198 | }; |
| 174 | 199 | |
| 175 | 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 | 215 | const toInstallationStoreShape = ( |
| 186 | 216 | input: BoundInstallationStore, |
| 187 | 217 | ): InstallationStoreShape => ({ |
| 188 | − load: () => input.load(), | |
| 189 | − getOrProvision: () => input.getOrProvision(), | |
| 218 | + load: input.load, | |
| 219 | + getOrProvision: input.getOrProvision, | |
| 190 | 220 | }); |
| 191 | 221 | |
| 192 | 222 | const toWorkspaceConfigStoreShape = ( |
| 193 | 223 | input: BoundWorkspaceConfigStore, |
| 194 | 224 | ): WorkspaceConfigStoreShape => ({ |
| 195 | − load: () => input.load(), | |
| 225 | + load: input.load, | |
| 196 | 226 | writeProject: ({ config }) => input.writeProject(config), |
| 197 | 227 | resolveRelativePath: input.resolveRelativePath, |
| 198 | 228 | }); |
| @@ -200,7 +230,7 @@ const toWorkspaceConfigStoreShape = ( · 1 unmodified line | ||
| 200 | 230 | const toWorkspaceStateStoreShape = ( |
| 201 | 231 | input: BoundWorkspaceStateStore, |
| 202 | 232 | ): WorkspaceStateStoreShape => ({ |
| 203 | − load: () => input.load(), | |
| 233 | + load: input.load, | |
| 204 | 234 | write: ({ state }) => input.write(state), |
| 205 | 235 | }); |
| 206 | 236 | |
| @@ -221,6 +251,9 @@ const makeSecretMaterialLayer = (input: RuntimeSecretMaterialServices) => · 14 unmodified lines | ||
| 221 | 251 | Layer.succeed(SecretMaterialUpdaterService, input.update), |
| 222 | 252 | ); |
| 223 | 253 | |
| 254 | +const makeInstanceConfigLayer = (input: RuntimeInstanceConfigService) => | |
| 255 | + Layer.succeed(LocalInstanceConfigService, input.resolve); | |
| 256 | + | |
| 224 | 257 | export const createRuntimeControlPlaneLayer = ( |
| 225 | 258 | input: RuntimeControlPlaneOptions & RuntimeControlPlaneServices & { |
| 226 | 259 | store: ControlPlaneStoreShape; |
| @@ -241,19 +274,30 @@ export const createRuntimeControlPlaneLayer = ( · 14 unmodified lines | ||
| 241 | 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 | 285 | const baseLayer = Layer.mergeAll( |
| 246 | 286 | Layer.succeed(ControlPlaneStore, input.store), |
| 247 | 287 | RuntimeLocalWorkspaceLive(input.localWorkspaceState), |
| 248 | 288 | storageLayer, |
| 249 | 289 | Layer.succeed(LiveExecutionManagerService, input.liveExecutionManager), |
| 290 | + sourceTypeDeclarationsRefresherLayer, | |
| 250 | 291 | ); |
| 251 | 292 | |
| 252 | 293 | const secretMaterialLayer = makeSecretMaterialLayer(input.secretMaterial).pipe( |
| 253 | 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 | 302 | const sourceCatalogStoreLayer = RuntimeSourceCatalogStoreLive.pipe( |
| 259 | 303 | Layer.provide(Layer.mergeAll(baseLayer, sourceStoreLayer)), |
| @@ -275,6 +319,7 @@ export const createRuntimeControlPlaneLayer = ( · 15 unmodified lines | ||
| 275 | 319 | Layer.provide( |
| 276 | 320 | Layer.mergeAll( |
| 277 | 321 | baseLayer, |
| 322 | + instanceConfigLayer, | |
| 278 | 323 | secretMaterialLayer, |
| 279 | 324 | sourceStoreLayer, |
| 280 | 325 | sourceCatalogSyncLayer, |
| @@ -289,6 +334,7 @@ export const createRuntimeControlPlaneLayer = ( · 8 unmodified lines | ||
| 289 | 334 | Layer.provide( |
| 290 | 335 | Layer.mergeAll( |
| 291 | 336 | baseLayer, |
| 337 | + instanceConfigLayer, | |
| 292 | 338 | secretMaterialLayer, |
| 293 | 339 | sourceStoreLayer, |
| 294 | 340 | sourceAuthMaterialLayer, |
| @@ -302,6 +348,7 @@ export const createRuntimeControlPlaneLayer = ( · 7 unmodified lines | ||
| 302 | 348 | |
| 303 | 349 | return Layer.mergeAll( |
| 304 | 350 | baseLayer, |
| 351 | + instanceConfigLayer, | |
| 305 | 352 | secretMaterialLayer, |
| 306 | 353 | sourceStoreLayer, |
| 307 | 354 | sourceAuthMaterialLayer, |
| @@ -314,7 +361,7 @@ export const createRuntimeControlPlaneLayer = ( · 6 unmodified lines | ||
| 314 | 361 | }; |
| 315 | 362 | |
| 316 | 363 | export type ControlPlaneRuntime = { |
| 317 | − persistence: LocalControlPlanePersistence; | |
| 364 | + persistence: RuntimePersistence; | |
| 318 | 365 | localInstallation: LocalInstallation; |
| 319 | 366 | managedRuntime: ManagedRuntime.ManagedRuntime<any, never>; |
| 320 | 367 | runtimeLayer: RuntimeControlPlaneLayer; |
| @@ -339,8 +386,12 @@ export const createControlPlaneRuntimeFromServices = (input: { · 18 unmodified lines | ||
| 339 | 386 | .load() |
| 340 | 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 | 394 | const effectiveLocalConfig = yield* synchronizeLocalWorkspaceState({ |
| 343 | − context: input.services.workspaceContext, | |
| 344 | 395 | loadedConfig: loadedLocalConfig, |
| 345 | 396 | }) |
| 346 | 397 | .pipe( |
| @@ -362,9 +413,8 @@ export const createControlPlaneRuntimeFromServices = (input: { · 15 unmodified lines | ||
| 362 | 413 | ), |
| 363 | 414 | ) |
| 364 | 415 | .pipe(Effect.mapError(toRuntimeBootstrapError)); |
| 365 | − | |
| 366 | 416 | const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = { |
| 367 | − context: input.services.workspaceContext, | |
| 417 | + workspace: runtimeWorkspace, | |
| 368 | 418 | installation: { |
| 369 | 419 | workspaceId: localInstallation.workspaceId, |
| 370 | 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 | 19 | import * as Cause from "effect/Cause"; |
| 20 | 20 | import * as Exit from "effect/Exit"; |
| 21 | 21 | import * as Schema from "effect/Schema"; |
| 22 | −import type { RuntimeLocalWorkspaceState } from "../local/runtime-context"; | |
| 22 | +import type { RuntimeLocalWorkspaceState } from "../workspace/runtime-context"; | |
| 23 | 23 | import { |
| 24 | 24 | type LocalStorageServices, |
| 25 | 25 | type InstallationStoreShape, |
| @@ -27,8 +27,8 @@ import { · 1 unmodified line | ||
| 27 | 27 | type WorkspaceConfigStoreShape, |
| 28 | 28 | type WorkspaceStateStoreShape, |
| 29 | 29 | makeLocalStorageLayer, |
| 30 | −} from "../local/storage"; | |
| 31 | −import { provideOptionalRuntimeLocalWorkspace } from "../local/runtime-context"; | |
| 30 | +} from "../workspace/storage"; | |
| 31 | +import { provideOptionalRuntimeLocalWorkspace } from "../workspace/runtime-context"; | |
| 32 | 32 | import { runtimeEffectError } from "../effect-errors"; |
| 33 | 33 | |
| 34 | 34 | /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */ |
| @@ -54,7 +54,7 @@ import { · 53 unmodified lines | ||
| 54 | 54 | getRuntimeLocalWorkspaceOption, |
| 55 | 55 | provideOptionalRuntimeLocalWorkspace, |
| 56 | 56 | type RuntimeLocalWorkspaceState, |
| 57 | −} from "../local/runtime-context"; | |
| 57 | +} from "../workspace/runtime-context"; | |
| 58 | 58 | import { |
| 59 | 59 | exchangeMcpOAuthAuthorizationCode, |
| 60 | 60 | startMcpOAuthAuthorization, |
| @@ -73,12 +73,13 @@ import { · 12 unmodified lines | ||
| 73 | 73 | sourceBindingStateFromSource, |
| 74 | 74 | } from "./source-adapters"; |
| 75 | 75 | import { |
| 76 | − createDefaultSecretMaterialDeleter, | |
| 76 | + type DeleteSecretMaterial, | |
| 77 | 77 | type ResolveSecretMaterial, |
| 78 | + SecretMaterialDeleterService, | |
| 78 | 79 | SecretMaterialResolverService, |
| 79 | 80 | SecretMaterialStorerService, |
| 80 | 81 | type StoreSecretMaterial, |
| 81 | −} from "../local/secret-material-providers"; | |
| 82 | +} from "../workspace/secret-material-providers"; | |
| 82 | 83 | import { |
| 83 | 84 | removeAuthLeaseAndSecrets, |
| 84 | 85 | upsertOauth2AuthorizedUserLeaseFromTokenResponse, |
| @@ -101,7 +102,7 @@ import { · 16 unmodified lines | ||
| 101 | 102 | type RuntimeSourceStore, |
| 102 | 103 | RuntimeSourceStoreService, |
| 103 | 104 | } from "./source-store"; |
| 104 | −import type { WorkspaceStorageServices } from "../local/storage"; | |
| 105 | +import type { WorkspaceStorageServices } from "../workspace/storage"; | |
| 105 | 106 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; |
| 106 | 107 | import { runtimeEffectError } from "../effect-errors"; |
| 107 | 108 | |
| @@ -994,6 +995,7 @@ const upsertSourceOauthClient = (input: { · 886 unmodified lines | ||
| 994 | 995 | source: Source; |
| 995 | 996 | oauthClient: SourceOauthClientInput; |
| 996 | 997 | storeSecretMaterial: StoreSecretMaterial; |
| 998 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 997 | 999 | }): Effect.Effect<ResolvedSourceOauthClient, Error, never> => |
| 998 | 1000 | Effect.gen(function* () { |
| 999 | 1001 | const adapter = getSourceAdapterForSource(input.source); |
| @@ -1052,10 +1054,7 @@ const upsertSourceOauthClient = (input: { · 52 unmodified lines | ||
| 1052 | 1054 | || previousClientSecretRef.handle !== clientSecretRef.handle |
| 1053 | 1055 | ) |
| 1054 | 1056 | ) { |
| 1055 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | |
| 1056 | − rows: input.rows, | |
| 1057 | − }); | |
| 1058 | − yield* deleteSecretMaterial(previousClientSecretRef).pipe( | |
| 1057 | + yield* input.deleteSecretMaterial(previousClientSecretRef).pipe( | |
| 1059 | 1058 | Effect.either, |
| 1060 | 1059 | Effect.ignore, |
| 1061 | 1060 | ); |
| @@ -1308,6 +1307,7 @@ const upsertProviderAuthGrant = (input: { · 246 unmodified lines | ||
| 1308 | 1307 | grantedScopes: ReadonlyArray<string>; |
| 1309 | 1308 | refreshToken: string | null; |
| 1310 | 1309 | storeSecretMaterial: StoreSecretMaterial; |
| 1310 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 1311 | 1311 | existingGrant?: import("#schema").ProviderAuthGrant | null; |
| 1312 | 1312 | }): Effect.Effect<import("#schema").ProviderAuthGrant, Error, never> => |
| 1313 | 1313 | Effect.gen(function* () { |
| @@ -1356,10 +1356,7 @@ const upsertProviderAuthGrant = (input: { · 42 unmodified lines | ||
| 1356 | 1356 | || existingGrant.refreshToken.handle !== refreshTokenRef.handle |
| 1357 | 1357 | ) |
| 1358 | 1358 | ) { |
| 1359 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | |
| 1360 | − rows: input.rows, | |
| 1361 | − }); | |
| 1362 | − yield* deleteSecretMaterial(existingGrant.refreshToken).pipe( | |
| 1359 | + yield* input.deleteSecretMaterial(existingGrant.refreshToken).pipe( | |
| 1363 | 1360 | Effect.either, |
| 1364 | 1361 | Effect.ignore, |
| 1365 | 1362 | ); |
| @@ -1747,6 +1744,7 @@ const removeProviderAuthGrantInternal = (input: { · 381 unmodified lines | ||
| 1747 | 1744 | sourceStore: RuntimeSourceStore; |
| 1748 | 1745 | workspaceId: WorkspaceId; |
| 1749 | 1746 | grantId: ProviderAuthGrant["id"]; |
| 1747 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 1750 | 1748 | }): Effect.Effect<boolean, Error, WorkspaceStorageServices> => |
| 1751 | 1749 | Effect.gen(function* () { |
| 1752 | 1750 | const grantOption = yield* input.rows.providerAuthGrants.getById(input.grantId); |
| @@ -1774,7 +1772,7 @@ const removeProviderAuthGrantInternal = (input: { · 21 unmodified lines | ||
| 1774 | 1772 | if (latestSource === null) { |
| 1775 | 1773 | yield* removeAuthLeaseAndSecrets(input.rows, { |
| 1776 | 1774 | authArtifactId: artifact.id, |
| 1777 | − }); | |
| 1775 | + }, input.deleteSecretMaterial); | |
| 1778 | 1776 | yield* input.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 1779 | 1777 | workspaceId: artifact.workspaceId, |
| 1780 | 1778 | sourceId: artifact.sourceId, |
| @@ -1800,7 +1798,7 @@ const removeProviderAuthGrantInternal = (input: { · 19 unmodified lines | ||
| 1800 | 1798 | |
| 1801 | 1799 | yield* removeProviderAuthGrantSecret(input.rows, { |
| 1802 | 1800 | grant: grantOption.value, |
| 1803 | − }); | |
| 1801 | + }, input.deleteSecretMaterial); | |
| 1804 | 1802 | yield* input.rows.providerAuthGrants.removeById(input.grantId); |
| 1805 | 1803 | return true; |
| 1806 | 1804 | }); |
| @@ -2093,6 +2091,7 @@ const addExecutorHttpSource = (input: { · 286 unmodified lines | ||
| 2093 | 2091 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2094 | 2092 | sourceInput: ExecutorHttpEndpointSourceInput; |
| 2095 | 2093 | storeSecretMaterial: StoreSecretMaterial; |
| 2094 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 2096 | 2095 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2097 | 2096 | getLocalServerBaseUrl?: () => string | undefined; |
| 2098 | 2097 | baseUrl?: string | null; |
| @@ -2305,6 +2304,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 206 unmodified lines | ||
| 2305 | 2304 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2306 | 2305 | sourceInput: Extract<ExecutorAddSourceInput, { kind: "google_discovery" }>; |
| 2307 | 2306 | storeSecretMaterial: StoreSecretMaterial; |
| 2307 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 2308 | 2308 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2309 | 2309 | getLocalServerBaseUrl?: () => string | undefined; |
| 2310 | 2310 | baseUrl?: string | null; |
| @@ -2491,6 +2491,7 @@ const addExecutorGoogleDiscoverySource = (input: { · 180 unmodified lines | ||
| 2491 | 2491 | source: persistedDraft, |
| 2492 | 2492 | oauthClient: input.sourceInput.oauthClient, |
| 2493 | 2493 | storeSecretMaterial: input.storeSecretMaterial, |
| 2494 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 2494 | 2495 | }); |
| 2495 | 2496 | } |
| 2496 | 2497 | |
| @@ -2820,6 +2821,7 @@ type RuntimeSourceAuthDependencies = { · 323 unmodified lines | ||
| 2820 | 2821 | sourceCatalogSync: RuntimeSourceCatalogSyncShape; |
| 2821 | 2822 | resolveSecretMaterial: ResolveSecretMaterial; |
| 2822 | 2823 | storeSecretMaterial: StoreSecretMaterial; |
| 2824 | + deleteSecretMaterial: DeleteSecretMaterial; | |
| 2823 | 2825 | getLocalServerBaseUrl?: () => string | undefined; |
| 2824 | 2826 | localWorkspaceState?: RuntimeLocalWorkspaceState; |
| 2825 | 2827 | }; |
| @@ -2880,6 +2882,7 @@ const createRuntimeSourceConnectionService = ( · 54 unmodified lines | ||
| 2880 | 2882 | sourceCatalogSync: input.sourceCatalogSync, |
| 2881 | 2883 | sourceInput, |
| 2882 | 2884 | storeSecretMaterial: input.storeSecretMaterial, |
| 2885 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 2883 | 2886 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 2884 | 2887 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 2885 | 2888 | baseUrl: options?.baseUrl, |
| @@ -2891,6 +2894,7 @@ const createRuntimeSourceConnectionService = ( · 5 unmodified lines | ||
| 2891 | 2894 | sourceCatalogSync: input.sourceCatalogSync, |
| 2892 | 2895 | sourceInput, |
| 2893 | 2896 | storeSecretMaterial: input.storeSecretMaterial, |
| 2897 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 2894 | 2898 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 2895 | 2899 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 2896 | 2900 | baseUrl: options?.baseUrl, |
| @@ -3014,10 +3018,7 @@ const createRuntimeSourceConnectionService = ( · 117 unmodified lines | ||
| 3014 | 3018 | const secretRef = sourceOauthClientSecretRef(oauthClient.value); |
| 3015 | 3019 | const removed = yield* input.rows.workspaceOauthClients.removeById(oauthClientId); |
| 3016 | 3020 | if (removed && secretRef) { |
| 3017 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ | |
| 3018 | − rows: input.rows, | |
| 3019 | − }); | |
| 3020 | − yield* deleteSecretMaterial(secretRef).pipe( | |
| 3021 | + yield* input.deleteSecretMaterial(secretRef).pipe( | |
| 3021 | 3022 | Effect.either, |
| 3022 | 3023 | Effect.ignore, |
| 3023 | 3024 | ); |
| @@ -3034,6 +3035,7 @@ const createRuntimeSourceConnectionService = ( · 10 unmodified lines | ||
| 3034 | 3035 | sourceStore: input.sourceStore, |
| 3035 | 3036 | workspaceId, |
| 3036 | 3037 | grantId, |
| 3038 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 3037 | 3039 | }), |
| 3038 | 3040 | ), |
| 3039 | 3041 | }; |
| @@ -3329,6 +3331,7 @@ const createRuntimeSourceOAuthSessionService = ( · 289 unmodified lines | ||
| 3329 | 3331 | refreshToken: trimOrNull(exchanged.refresh_token), |
| 3330 | 3332 | existingGrant, |
| 3331 | 3333 | storeSecretMaterial: input.storeSecretMaterial, |
| 3334 | + deleteSecretMaterial: input.deleteSecretMaterial, | |
| 3332 | 3335 | }); |
| 3333 | 3336 | |
| 3334 | 3337 | const targets = yield* Effect.forEach( |
| @@ -3526,6 +3529,8 @@ const createRuntimeSourceOAuthSessionService = ( · 191 unmodified lines | ||
| 3526 | 3529 | rows: input.rows, |
| 3527 | 3530 | artifact: authArtifact.value, |
| 3528 | 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 | 3703 | const sourceCatalogSync = yield* RuntimeSourceCatalogSyncService; |
| 3699 | 3704 | const resolveSecretMaterial = yield* SecretMaterialResolverService; |
| 3700 | 3705 | const storeSecretMaterial = yield* SecretMaterialStorerService; |
| 3706 | + const deleteSecretMaterial = yield* SecretMaterialDeleterService; | |
| 3701 | 3707 | const runtimeLocalWorkspace = yield* getRuntimeLocalWorkspaceOption(); |
| 3702 | 3708 | |
| 3703 | 3709 | return createRuntimeSourceAuthService({ |
| @@ -3707,6 +3713,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 3 unmodified lines | ||
| 3707 | 3713 | sourceCatalogSync, |
| 3708 | 3714 | resolveSecretMaterial, |
| 3709 | 3715 | storeSecretMaterial, |
| 3716 | + deleteSecretMaterial, | |
| 3710 | 3717 | getLocalServerBaseUrl: input.getLocalServerBaseUrl, |
| 3711 | 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 | 7 | type Source, |
| 8 | 8 | } from "#schema"; |
| 9 | 9 | |
| 10 | −import type { LoadedLocalExecutorConfig } from "../local/config"; | |
| 10 | +import type { LoadedLocalExecutorConfig } from "../workspace-config"; | |
| 11 | 11 | import { buildLocalSourceRecord } from "./source-store"; |
| 12 | −import type { LocalWorkspaceState } from "../local/workspace-state"; | |
| 12 | +import type { LocalWorkspaceState } from "../workspace-state"; | |
| 13 | 13 | |
| 14 | 14 | const workspaceId = WorkspaceIdSchema.make("ws_source_store"); |
| 15 | 15 | const sourceId = SourceIdSchema.make("linear"); |
| @@ -7,13 +7,15 @@ import * as Context from "effect/Context"; · 6 unmodified lines | ||
| 7 | 7 | import * as Effect from "effect/Effect"; |
| 8 | 8 | import * as Layer from "effect/Layer"; |
| 9 | 9 | |
| 10 | +import { SourceTypeDeclarationsRefresherService } from "../catalog/source/type-declarations"; | |
| 10 | 11 | import { |
| 11 | 12 | SourceArtifactStore, |
| 12 | 13 | type WorkspaceStorageServices, |
| 13 | 14 | WorkspaceConfigStore, |
| 14 | 15 | WorkspaceStateStore, |
| 15 | −} from "../local/storage"; | |
| 16 | −import { RuntimeLocalWorkspaceService } from "../local/runtime-context"; | |
| 16 | +} from "../workspace/storage"; | |
| 17 | +import { SecretMaterialDeleterService } from "../workspace/secret-material-providers"; | |
| 18 | +import { RuntimeLocalWorkspaceService } from "../workspace/runtime-context"; | |
| 17 | 19 | import { ControlPlaneStore, type ControlPlaneStoreShape } from "../store"; |
| 18 | 20 | import { |
| 19 | 21 | loadRuntimeSourceStoreDeps, |
| @@ -64,7 +66,11 @@ export const loadSourcesInWorkspace = ( · 44 unmodified lines | ||
| 64 | 66 | options: { |
| 65 | 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 | 74 | Effect.flatMap( |
| 69 | 75 | loadRuntimeSourceStoreDeps(rows, workspaceId), |
| 70 | 76 | (deps) => loadSourcesInWorkspaceWithDeps(deps, workspaceId, options), |
| @@ -79,7 +85,7 @@ export const listLinkedSecretSourcesInWorkspace = ( · 8 unmodified lines | ||
| 79 | 85 | ): Effect.Effect< |
| 80 | 86 | Map<string, Array<{ sourceId: string; sourceName: string }>>, |
| 81 | 87 | Error, |
| 82 | − WorkspaceStorageServices | |
| 88 | + WorkspaceStorageServices | SourceTypeDeclarationsRefresherService | |
| 83 | 89 | > => |
| 84 | 90 | Effect.flatMap( |
| 85 | 91 | loadRuntimeSourceStoreDeps(rows, workspaceId), |
| @@ -93,7 +99,11 @@ export const loadSourceById = ( · 7 unmodified lines | ||
| 93 | 99 | sourceId: Source["id"]; |
| 94 | 100 | actorAccountId?: AccountId | null; |
| 95 | 101 | }, |
| 96 | −): Effect.Effect<Source, Error, WorkspaceStorageServices> => | |
| 102 | +): Effect.Effect< | |
| 103 | + Source, | |
| 104 | + Error, | |
| 105 | + WorkspaceStorageServices | SourceTypeDeclarationsRefresherService | |
| 106 | +> => | |
| 97 | 107 | Effect.flatMap( |
| 98 | 108 | loadRuntimeSourceStoreDeps(rows, input.workspaceId), |
| 99 | 109 | (deps) => loadSourceByIdWithDeps(deps, input), |
| @@ -105,11 +115,18 @@ export const removeSourceById = ( · 5 unmodified lines | ||
| 105 | 115 | workspaceId: WorkspaceId; |
| 106 | 116 | sourceId: Source["id"]; |
| 107 | 117 | }, |
| 108 | −): Effect.Effect<boolean, Error, WorkspaceStorageServices> => | |
| 109 | − Effect.flatMap( | |
| 110 | − loadRuntimeSourceStoreDeps(rows, input.workspaceId), | |
| 111 | − (deps) => removeSourceByIdWithDeps(deps, input), | |
| 112 | − ); | |
| 118 | +): Effect.Effect< | |
| 119 | + boolean, | |
| 120 | + Error, | |
| 121 | + | WorkspaceStorageServices | |
| 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 | 131 | export const persistSource = ( |
| 115 | 132 | rows: ControlPlaneStoreShape, |
| @@ -117,11 +134,18 @@ export const persistSource = ( · 1 unmodified line | ||
| 117 | 134 | options: { |
| 118 | 135 | actorAccountId?: AccountId | null; |
| 119 | 136 | } = {}, |
| 120 | −): Effect.Effect<Source, Error, WorkspaceStorageServices> => | |
| 121 | − Effect.flatMap( | |
| 122 | − loadRuntimeSourceStoreDeps(rows, source.workspaceId), | |
| 123 | − (deps) => persistSourceWithDeps(deps, source, options), | |
| 124 | − ); | |
| 137 | +): Effect.Effect< | |
| 138 | + Source, | |
| 139 | + Error, | |
| 140 | + | WorkspaceStorageServices | |
| 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 | 150 | export class RuntimeSourceStoreService extends Context.Tag( |
| 127 | 151 | "#runtime/RuntimeSourceStoreService", |
| @@ -135,6 +159,9 @@ export const RuntimeSourceStoreLive = Layer.effect( · 7 unmodified lines | ||
| 135 | 159 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 136 | 160 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 137 | 161 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 162 | + const sourceTypeDeclarationsRefresher = | |
| 163 | + yield* SourceTypeDeclarationsRefresherService; | |
| 164 | + const deleteSecretMaterial = yield* SecretMaterialDeleterService; | |
| 138 | 165 | |
| 139 | 166 | const deps: RuntimeSourceStoreDeps = { |
| 140 | 167 | rows, |
| @@ -142,6 +169,7 @@ export const RuntimeSourceStoreLive = Layer.effect( · 1 unmodified line | ||
| 142 | 169 | workspaceConfigStore, |
| 143 | 170 | workspaceStateStore, |
| 144 | 171 | sourceArtifactStore, |
| 172 | + sourceTypeDeclarationsRefresher, | |
| 145 | 173 | }; |
| 146 | 174 | |
| 147 | 175 | return RuntimeSourceStoreService.of({ |
| @@ -152,9 +180,9 @@ export const RuntimeSourceStoreLive = Layer.effect( · 4 unmodified lines | ||
| 152 | 180 | loadSourceById: (input) => |
| 153 | 181 | loadSourceByIdWithDeps(deps, input), |
| 154 | 182 | removeSourceById: (input) => |
| 155 | − removeSourceByIdWithDeps(deps, input), | |
| 183 | + removeSourceByIdWithDeps(deps, input, deleteSecretMaterial), | |
| 156 | 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 | 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; |
| 13 | 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 | 15 | import type { ControlPlaneStoreShape } from "../../store"; |
| 16 | 16 | |
| 17 | 17 | const secretRefKey = (ref: { providerId: string; handle: string }): string => |
| @@ -23,13 +23,12 @@ export const cleanupAuthArtifactSecretRefs = ( · 5 unmodified lines | ||
| 23 | 23 | previous: AuthArtifact | null; |
| 24 | 24 | next: AuthArtifact | null; |
| 25 | 25 | }, |
| 26 | + deleteSecretMaterial: DeleteSecretMaterial, | |
| 26 | 27 | ) => |
| 27 | 28 | Effect.gen(function* () { |
| 28 | 29 | if (input.previous === null) { |
| 29 | 30 | return; |
| 30 | 31 | } |
| 31 | − | |
| 32 | − const deleteSecretMaterial = createDefaultSecretMaterialDeleter({ rows }); | |
| 33 | 32 | const nextRefKeys = new Set( |
| 34 | 33 | (input.next === null ? [] : authArtifactSecretMaterialRefs(input.next)).map( |
| 35 | 34 | secretRefKey, |
| @@ -97,6 +96,7 @@ export const removeAuthArtifactsForSource = ( · 61 unmodified lines | ||
| 97 | 96 | workspaceId: WorkspaceId; |
| 98 | 97 | sourceId: Source["id"]; |
| 99 | 98 | }, |
| 99 | + deleteSecretMaterial: DeleteSecretMaterial, | |
| 100 | 100 | ) => |
| 101 | 101 | Effect.gen(function* () { |
| 102 | 102 | const existingAuthArtifacts = yield* rows.authArtifacts.listByWorkspaceAndSourceId({ |
| @@ -114,7 +114,7 @@ export const removeAuthArtifactsForSource = ( · 11 unmodified lines | ||
| 114 | 114 | (artifact) => |
| 115 | 115 | removeAuthLeaseAndSecrets(rows, { |
| 116 | 116 | authArtifactId: artifact.id, |
| 117 | − }), | |
| 117 | + }, deleteSecretMaterial), | |
| 118 | 118 | { discard: true }, |
| 119 | 119 | ); |
| 120 | 120 | |
| @@ -124,7 +124,7 @@ export const removeAuthArtifactsForSource = ( · 3 unmodified lines | ||
| 124 | 124 | cleanupAuthArtifactSecretRefs(rows, { |
| 125 | 125 | previous: artifact, |
| 126 | 126 | next: null, |
| 127 | − }), | |
| 127 | + }, deleteSecretMaterial), | |
| 128 | 128 | { discard: true }, |
| 129 | 129 | ); |
| 130 | 130 | |
| @@ -6,12 +6,12 @@ import type { · 5 unmodified lines | ||
| 6 | 6 | } from "#schema"; |
| 7 | 7 | import { SourceIdSchema } from "#schema"; |
| 8 | 8 | |
| 9 | −import type { LoadedLocalExecutorConfig } from "../../local/config"; | |
| 10 | −import { LocalUnsupportedSourceKindError } from "../../local/errors"; | |
| 9 | +import type { LoadedLocalExecutorConfig } from "../../workspace-config"; | |
| 10 | +import { LocalUnsupportedSourceKindError } from "../../workspace-errors"; | |
| 11 | 11 | import { |
| 12 | 12 | fromConfigSecretProviderId, |
| 13 | 13 | toConfigSecretProviderId, |
| 14 | −} from "../../local/config-secrets"; | |
| 14 | +} from "../../workspace/config-secrets"; | |
| 15 | 15 | import { getSourceAdapterForSource } from "../source-adapters"; |
| 16 | 16 | import { slugify } from "../slug"; |
| 17 | 17 | |
| @@ -1,33 +1,31 @@ | ||
| 1 | 1 | import type { AccountId, WorkspaceId } from "#schema"; |
| 2 | 2 | import * as Effect from "effect/Effect"; |
| 3 | 3 | |
| 4 | −import type { | |
| 5 | − LoadedLocalExecutorConfig, | |
| 6 | − ResolvedLocalWorkspaceContext, | |
| 7 | −} from "../../local/config"; | |
| 4 | +import type { LoadedLocalExecutorConfig } from "../../workspace-config"; | |
| 5 | +import { | |
| 6 | + SourceTypeDeclarationsRefresherService, | |
| 7 | + type SourceTypeDeclarationsRefresherShape, | |
| 8 | +} from "../../catalog/source/type-declarations"; | |
| 8 | 9 | import { |
| 9 | − LocalExecutorConfigDecodeError, | |
| 10 | − LocalFileSystemError, | |
| 11 | − LocalWorkspaceStateDecodeError, | |
| 12 | 10 | RuntimeLocalWorkspaceMismatchError, |
| 13 | 11 | RuntimeLocalWorkspaceUnavailableError, |
| 14 | −} from "../../local/errors"; | |
| 12 | +} from "../../workspace-errors"; | |
| 15 | 13 | import { |
| 16 | 14 | requireRuntimeLocalWorkspace, |
| 17 | 15 | type RuntimeLocalWorkspaceState, |
| 18 | −} from "../../local/runtime-context"; | |
| 16 | +} from "../../workspace/runtime-context"; | |
| 19 | 17 | import type { |
| 20 | 18 | SourceArtifactStoreShape, |
| 21 | 19 | WorkspaceStorageServices, |
| 22 | 20 | WorkspaceConfigStoreShape, |
| 23 | 21 | WorkspaceStateStoreShape, |
| 24 | −} from "../../local/storage"; | |
| 22 | +} from "../../workspace/storage"; | |
| 25 | 23 | import { |
| 26 | 24 | SourceArtifactStore, |
| 27 | 25 | WorkspaceConfigStore, |
| 28 | 26 | WorkspaceStateStore, |
| 29 | −} from "../../local/storage"; | |
| 30 | −import type { LocalWorkspaceState } from "../../local/workspace-state"; | |
| 27 | +} from "../../workspace/storage"; | |
| 28 | +import type { LocalWorkspaceState } from "../../workspace-state"; | |
| 31 | 29 | import type { ControlPlaneStoreShape } from "../../store"; |
| 32 | 30 | |
| 33 | 31 | export type RuntimeSourceStoreDeps = { |
| @@ -36,10 +34,10 @@ export type RuntimeSourceStoreDeps = { · 2 unmodified lines | ||
| 36 | 34 | workspaceConfigStore: WorkspaceConfigStoreShape; |
| 37 | 35 | workspaceStateStore: WorkspaceStateStoreShape; |
| 38 | 36 | sourceArtifactStore: SourceArtifactStoreShape; |
| 37 | + sourceTypeDeclarationsRefresher: SourceTypeDeclarationsRefresherShape; | |
| 39 | 38 | }; |
| 40 | 39 | |
| 41 | 40 | export type ResolvedSourceStoreWorkspace = { |
| 42 | − context: ResolvedLocalWorkspaceContext; | |
| 43 | 41 | installation: { |
| 44 | 42 | workspaceId: WorkspaceId; |
| 45 | 43 | accountId: AccountId; |
| @@ -51,6 +49,9 @@ export type ResolvedSourceStoreWorkspace = { · 5 unmodified lines | ||
| 51 | 49 | workspaceState: LocalWorkspaceState; |
| 52 | 50 | }; |
| 53 | 51 | |
| 52 | +export type RuntimeSourceStoreServices = | |
| 53 | + WorkspaceStorageServices | SourceTypeDeclarationsRefresherService; | |
| 54 | + | |
| 54 | 55 | export const resolveRuntimeLocalWorkspaceFromDeps = ( |
| 55 | 56 | deps: RuntimeSourceStoreDeps, |
| 56 | 57 | workspaceId: WorkspaceId, |
| @@ -58,9 +59,6 @@ export const resolveRuntimeLocalWorkspaceFromDeps = ( · 1 unmodified line | ||
| 58 | 59 | ResolvedSourceStoreWorkspace, |
| 59 | 60 | | RuntimeLocalWorkspaceUnavailableError |
| 60 | 61 | | RuntimeLocalWorkspaceMismatchError |
| 61 | − | LocalFileSystemError | |
| 62 | − | LocalExecutorConfigDecodeError | |
| 63 | − | LocalWorkspaceStateDecodeError | |
| 64 | 62 | | Error, |
| 65 | 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( | |
| 77 | − deps.runtimeLocalWorkspace.context, | |
| 78 | − ); | |
| 79 | − const workspaceState = yield* deps.workspaceStateStore.load( | |
| 80 | − deps.runtimeLocalWorkspace.context, | |
| 81 | − ); | |
| 74 | + const loadedConfig = yield* deps.workspaceConfigStore.load(); | |
| 75 | + const workspaceState = yield* deps.workspaceStateStore.load(); | |
| 82 | 76 | |
| 83 | 77 | return { |
| 84 | − context: deps.runtimeLocalWorkspace.context, | |
| 85 | 78 | installation: deps.runtimeLocalWorkspace.installation, |
| 86 | 79 | workspaceConfigStore: deps.workspaceConfigStore, |
| 87 | 80 | workspaceStateStore: deps.workspaceStateStore, |
| @@ -98,17 +91,16 @@ export const loadRuntimeSourceStoreDeps = ( · 10 unmodified lines | ||
| 98 | 91 | RuntimeSourceStoreDeps, |
| 99 | 92 | | RuntimeLocalWorkspaceUnavailableError |
| 100 | 93 | | RuntimeLocalWorkspaceMismatchError |
| 101 | − | LocalFileSystemError | |
| 102 | − | LocalExecutorConfigDecodeError | |
| 103 | − | LocalWorkspaceStateDecodeError | |
| 104 | 94 | | Error, |
| 105 | − WorkspaceStorageServices | |
| 95 | + RuntimeSourceStoreServices | |
| 106 | 96 | > => |
| 107 | 97 | Effect.gen(function* () { |
| 108 | 98 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 109 | 99 | const workspaceConfigStore = yield* WorkspaceConfigStore; |
| 110 | 100 | const workspaceStateStore = yield* WorkspaceStateStore; |
| 111 | 101 | const sourceArtifactStore = yield* SourceArtifactStore; |
| 102 | + const sourceTypeDeclarationsRefresher = | |
| 103 | + yield* SourceTypeDeclarationsRefresherService; | |
| 112 | 104 | |
| 113 | 105 | return { |
| 114 | 106 | rows, |
| @@ -116,5 +108,6 @@ export const loadRuntimeSourceStoreDeps = ( · 1 unmodified line | ||
| 116 | 108 | workspaceConfigStore, |
| 117 | 109 | workspaceStateStore, |
| 118 | 110 | sourceArtifactStore, |
| 111 | + sourceTypeDeclarationsRefresher, | |
| 119 | 112 | }; |
| 120 | 113 | }); |
| @@ -6,7 +6,8 @@ import { · 5 unmodified lines | ||
| 6 | 6 | clearProviderGrantOrphanedAt, |
| 7 | 7 | markProviderGrantOrphanedIfUnused, |
| 8 | 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 | 11 | import { |
| 11 | 12 | stableSourceCatalogId, |
| 12 | 13 | stableSourceCatalogRevisionId, |
| @@ -39,6 +40,7 @@ export const removeSourceByIdWithDeps = ( · 26 unmodified lines | ||
| 39 | 40 | workspaceId: WorkspaceId; |
| 40 | 41 | sourceId: Source["id"]; |
| 41 | 42 | }, |
| 43 | + deleteSecretMaterial: DeleteSecretMaterial, | |
| 42 | 44 | ): Effect.Effect<boolean, Error, never> => |
| 43 | 45 | Effect.gen(function* () { |
| 44 | 46 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( |
| @@ -55,7 +57,6 @@ export const removeSourceByIdWithDeps = ( · 10 unmodified lines | ||
| 55 | 57 | }; |
| 56 | 58 | delete sources[input.sourceId]; |
| 57 | 59 | yield* localWorkspace.workspaceConfigStore.writeProject({ |
| 58 | − context: localWorkspace.context, | |
| 59 | 60 | config: { |
| 60 | 61 | ...projectConfig, |
| 61 | 62 | sources, |
| @@ -69,11 +70,9 @@ export const removeSourceByIdWithDeps = ( · 7 unmodified lines | ||
| 69 | 70 | sources: remainingSources, |
| 70 | 71 | }; |
| 71 | 72 | yield* localWorkspace.workspaceStateStore.write({ |
| 72 | − context: localWorkspace.context, | |
| 73 | 73 | state: workspaceState, |
| 74 | 74 | }); |
| 75 | 75 | yield* localWorkspace.sourceArtifactStore.remove({ |
| 76 | − context: localWorkspace.context, | |
| 77 | 76 | sourceId: input.sourceId, |
| 78 | 77 | }); |
| 79 | 78 | const existingAuthArtifacts = |
| @@ -91,7 +90,7 @@ export const removeSourceByIdWithDeps = ( · 11 unmodified lines | ||
| 91 | 90 | workspaceId: input.workspaceId, |
| 92 | 91 | sourceId: input.sourceId, |
| 93 | 92 | }); |
| 94 | − yield* removeAuthArtifactsForSource(deps.rows, input); | |
| 93 | + yield* removeAuthArtifactsForSource(deps.rows, input, deleteSecretMaterial); | |
| 95 | 94 | yield* Effect.forEach( |
| 96 | 95 | [...removedGrantIds], |
| 97 | 96 | (grantId) => |
| @@ -112,6 +111,7 @@ export const persistSourceWithDeps = ( · 14 unmodified lines | ||
| 112 | 111 | options: { |
| 113 | 112 | actorAccountId?: AccountId | null; |
| 114 | 113 | } = {}, |
| 114 | + deleteSecretMaterial: DeleteSecretMaterial, | |
| 115 | 115 | ): Effect.Effect<Source, Error, never> => |
| 116 | 116 | Effect.gen(function* () { |
| 117 | 117 | const localWorkspace = yield* resolveRuntimeLocalWorkspaceFromDeps( |
| @@ -155,7 +155,6 @@ export const persistSourceWithDeps = ( · 37 unmodified lines | ||
| 155 | 155 | config: localWorkspace.loadedConfig.config, |
| 156 | 156 | }); |
| 157 | 157 | yield* localWorkspace.workspaceConfigStore.writeProject({ |
| 158 | − context: localWorkspace.context, | |
| 159 | 158 | config: { |
| 160 | 159 | ...projectConfig, |
| 161 | 160 | sources, |
| @@ -175,7 +174,7 @@ export const persistSourceWithDeps = ( · 13 unmodified lines | ||
| 175 | 174 | if (existingRuntimeAuthArtifact !== null) { |
| 176 | 175 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 177 | 176 | authArtifactId: existingRuntimeAuthArtifact.id, |
| 178 | − }); | |
| 177 | + }, deleteSecretMaterial); | |
| 179 | 178 | } |
| 180 | 179 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 181 | 180 | workspaceId: nextSource.workspaceId, |
| @@ -191,20 +190,20 @@ export const persistSourceWithDeps = ( · 9 unmodified lines | ||
| 191 | 190 | ) { |
| 192 | 191 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 193 | 192 | authArtifactId: existingRuntimeAuthArtifact.id, |
| 194 | − }); | |
| 193 | + }, deleteSecretMaterial); | |
| 195 | 194 | } |
| 196 | 195 | } |
| 197 | 196 | |
| 198 | 197 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { |
| 199 | 198 | previous: existingRuntimeAuthArtifact ?? null, |
| 200 | 199 | next: runtimeAuthArtifact, |
| 201 | − }); | |
| 200 | + }, deleteSecretMaterial); | |
| 202 | 201 | |
| 203 | 202 | if (importAuthArtifact === null) { |
| 204 | 203 | if (existingImportAuthArtifact !== null) { |
| 205 | 204 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 206 | 205 | authArtifactId: existingImportAuthArtifact.id, |
| 207 | − }); | |
| 206 | + }, deleteSecretMaterial); | |
| 208 | 207 | } |
| 209 | 208 | yield* deps.rows.authArtifacts.removeByWorkspaceSourceAndActor({ |
| 210 | 209 | workspaceId: nextSource.workspaceId, |
| @@ -220,14 +219,14 @@ export const persistSourceWithDeps = ( · 9 unmodified lines | ||
| 220 | 219 | ) { |
| 221 | 220 | yield* removeAuthLeaseAndSecrets(deps.rows, { |
| 222 | 221 | authArtifactId: existingImportAuthArtifact.id, |
| 223 | − }); | |
| 222 | + }, deleteSecretMaterial); | |
| 224 | 223 | } |
| 225 | 224 | } |
| 226 | 225 | |
| 227 | 226 | yield* cleanupAuthArtifactSecretRefs(deps.rows, { |
| 228 | 227 | previous: existingImportAuthArtifact ?? null, |
| 229 | 228 | next: importAuthArtifact, |
| 230 | − }); | |
| 229 | + }, deleteSecretMaterial); | |
| 231 | 230 | |
| 232 | 231 | const previousGrantIds = providerGrantIdsFromArtifacts([ |
| 233 | 232 | existingRuntimeAuthArtifact, |
| @@ -271,7 +270,6 @@ export const persistSourceWithDeps = ( · 37 unmodified lines | ||
| 271 | 270 | }, |
| 272 | 271 | }; |
| 273 | 272 | yield* localWorkspace.workspaceStateStore.write({ |
| 274 | − context: localWorkspace.context, | |
| 275 | 273 | state: workspaceState, |
| 276 | 274 | }); |
| 277 | 275 | |
| @@ -10,17 +10,13 @@ import * as Effect from "effect/Effect"; · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | import { sourceAuthFromAuthArtifact } from "../../auth/auth-artifacts"; |
| 12 | 12 | import { authArtifactSecretMaterialRefs } from "../../auth/auth-artifacts"; |
| 13 | −import { refreshWorkspaceSourceTypeDeclarationsInBackground } from "../../catalog/source/type-declarations"; | |
| 14 | −import type { LoadedLocalExecutorConfig } from "../../local/config"; | |
| 13 | +import type { LoadedLocalExecutorConfig } from "../../workspace-config"; | |
| 15 | 14 | import { |
| 16 | 15 | LocalConfiguredSourceNotFoundError, |
| 17 | − LocalExecutorConfigDecodeError, | |
| 18 | − LocalFileSystemError, | |
| 19 | − LocalWorkspaceStateDecodeError, | |
| 20 | 16 | RuntimeLocalWorkspaceMismatchError, |
| 21 | 17 | RuntimeLocalWorkspaceUnavailableError, |
| 22 | −} from "../../local/errors"; | |
| 23 | −import type { LocalWorkspaceState } from "../../local/workspace-state"; | |
| 18 | +} from "../../workspace-errors"; | |
| 19 | +import type { LocalWorkspaceState } from "../../workspace-state"; | |
| 24 | 20 | import { getSourceAdapter } from "../source-adapters"; |
| 25 | 21 | import { |
| 26 | 22 | resolveRuntimeLocalWorkspaceFromDeps, |
| @@ -131,9 +127,6 @@ export const loadSourcesInWorkspaceWithDeps = ( · 104 unmodified lines | ||
| 131 | 127 | readonly Source[], |
| 132 | 128 | | RuntimeLocalWorkspaceUnavailableError |
| 133 | 129 | | RuntimeLocalWorkspaceMismatchError |
| 134 | − | LocalFileSystemError | |
| 135 | − | LocalExecutorConfigDecodeError | |
| 136 | − | LocalWorkspaceStateDecodeError | |
| 137 | 130 | | LocalConfiguredSourceNotFoundError |
| 138 | 131 | | Error, |
| 139 | 132 | never |
| @@ -191,7 +184,6 @@ export const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 51 unmodified lines | ||
| 191 | 184 | const entries = yield* Effect.forEach(sources, (source) => |
| 192 | 185 | Effect.map( |
| 193 | 186 | deps.sourceArtifactStore.read({ |
| 194 | − context: localWorkspace.context, | |
| 195 | 187 | sourceId: source.id, |
| 196 | 188 | }), |
| 197 | 189 | (artifact) => |
| @@ -204,13 +196,10 @@ export const syncWorkspaceSourceTypeDeclarationsWithDeps = ( · 6 unmodified lines | ||
| 204 | 196 | ), |
| 205 | 197 | ); |
| 206 | 198 | |
| 207 | − yield* Effect.sync(() => { | |
| 208 | − refreshWorkspaceSourceTypeDeclarationsInBackground({ | |
| 209 | − context: localWorkspace.context, | |
| 210 | − entries: entries.filter( | |
| 211 | − (entry): entry is NonNullable<typeof entry> => entry !== null, | |
| 212 | − ), | |
| 213 | − }); | |
| 199 | + yield* deps.sourceTypeDeclarationsRefresher.refreshWorkspaceInBackground({ | |
| 200 | + entries: entries.filter( | |
| 201 | + (entry): entry is NonNullable<typeof entry> => entry !== null, | |
| 202 | + ), | |
| 214 | 203 | }); |
| 215 | 204 | }).pipe( |
| 216 | 205 | Effect.withSpan("source.types.refresh_workspace.schedule", { |
| @@ -236,9 +225,6 @@ export const listLinkedSecretSourcesInWorkspaceWithDeps = ( · 19 unmodified lines | ||
| 236 | 225 | Map<string, Array<{ sourceId: string; sourceName: string }>>, |
| 237 | 226 | | RuntimeLocalWorkspaceUnavailableError |
| 238 | 227 | | RuntimeLocalWorkspaceMismatchError |
| 239 | − | LocalFileSystemError | |
| 240 | − | LocalExecutorConfigDecodeError | |
| 241 | − | LocalWorkspaceStateDecodeError | |
| 242 | 228 | | LocalConfiguredSourceNotFoundError |
| 243 | 229 | | Error, |
| 244 | 230 | never |
| @@ -295,9 +281,6 @@ export const loadSourceByIdWithDeps = ( · 50 unmodified lines | ||
| 295 | 281 | Source, |
| 296 | 282 | | RuntimeLocalWorkspaceUnavailableError |
| 297 | 283 | | RuntimeLocalWorkspaceMismatchError |
| 298 | − | LocalFileSystemError | |
| 299 | − | LocalExecutorConfigDecodeError | |
| 300 | − | LocalWorkspaceStateDecodeError | |
| 301 | 284 | | LocalConfiguredSourceNotFoundError |
| 302 | 285 | | Error, |
| 303 | 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 | 4 | import * as Option from "effect/Option"; |
| 5 | 5 | |
| 6 | 6 | import type { AccountId, WorkspaceId } from "#schema"; |
| 7 | −import type { | |
| 8 | − LoadedLocalExecutorConfig, | |
| 9 | − ResolvedLocalWorkspaceContext, | |
| 10 | −} from "./config"; | |
| 7 | +import type { ExecutorWorkspaceContext } from "../../workspace"; | |
| 8 | +import type { LoadedLocalExecutorConfig } from "../workspace-config"; | |
| 11 | 9 | import { |
| 12 | 10 | RuntimeLocalWorkspaceMismatchError, |
| 13 | 11 | RuntimeLocalWorkspaceUnavailableError, |
| 14 | −} from "./errors"; | |
| 12 | +} from "../workspace-errors"; | |
| 15 | 13 | |
| 16 | 14 | export type RuntimeLocalWorkspaceState = { |
| 17 | − context: ResolvedLocalWorkspaceContext; | |
| 15 | + workspace: ExecutorWorkspaceContext; | |
| 18 | 16 | installation: { |
| 19 | 17 | workspaceId: WorkspaceId; |
| 20 | 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 | 1 | import * as Effect from "effect/Effect"; |
| 3 | 2 | |
| 4 | −import { | |
| 5 | − type LoadedLocalExecutorConfig, | |
| 6 | − type ResolvedLocalWorkspaceContext, | |
| 7 | −} from "./config"; | |
| 8 | −import { | |
| 9 | − loadLocalWorkspaceState, | |
| 10 | − writeLocalWorkspaceState, | |
| 11 | − type LocalWorkspaceState, | |
| 12 | −} from "./workspace-state"; | |
| 3 | +import { type LoadedLocalExecutorConfig } from "../workspace-config"; | |
| 4 | +import { WorkspaceStateStore } from "./storage"; | |
| 5 | +import { type LocalWorkspaceState } from "../workspace-state"; | |
| 13 | 6 | |
| 14 | 7 | const trimOrNull = (value: string | null | undefined): string | null => { |
| 15 | 8 | if (value == null) { |
| @@ -41,11 +34,11 @@ export const derivePolicyConfigKey = ( · 25 unmodified lines | ||
| 41 | 34 | }; |
| 42 | 35 | |
| 43 | 36 | const pruneLocalWorkspaceState = (input: { |
| 44 | − context: ResolvedLocalWorkspaceContext; | |
| 45 | 37 | loadedConfig: LoadedLocalExecutorConfig; |
| 46 | −}): Effect.Effect<LocalWorkspaceState, Error, FileSystem.FileSystem> => | |
| 38 | +}): Effect.Effect<LocalWorkspaceState, Error, WorkspaceStateStore> => | |
| 47 | 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 | 43 | const configuredSourceIds = new Set( |
| 51 | 44 | Object.keys(input.loadedConfig.config?.sources ?? {}), |
| @@ -72,8 +65,7 @@ const pruneLocalWorkspaceState = (input: { · 20 unmodified lines | ||
| 72 | 65 | return currentState; |
| 73 | 66 | } |
| 74 | 67 | |
| 75 | − yield* writeLocalWorkspaceState({ | |
| 76 | − context: input.context, | |
| 68 | + yield* workspaceStateStore.write({ | |
| 77 | 69 | state: nextState, |
| 78 | 70 | }); |
| 79 | 71 | |
| @@ -81,12 +73,10 @@ const pruneLocalWorkspaceState = (input: { · 1 unmodified line | ||
| 81 | 73 | }); |
| 82 | 74 | |
| 83 | 75 | export const synchronizeLocalWorkspaceState = (input: { |
| 84 | − context: ResolvedLocalWorkspaceContext; | |
| 85 | 76 | loadedConfig: LoadedLocalExecutorConfig; |
| 86 | −}): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, FileSystem.FileSystem> => | |
| 77 | +}): Effect.Effect<LoadedLocalExecutorConfig["config"], Error, WorkspaceStateStore> => | |
| 87 | 78 | Effect.gen(function* () { |
| 88 | 79 | yield* pruneLocalWorkspaceState({ |
| 89 | − context: input.context, | |
| 90 | 80 | loadedConfig: input.loadedConfig, |
| 91 | 81 | }); |
| 92 | 82 | |
| @@ -16,7 +16,7 @@ import { · 15 unmodified lines | ||
| 16 | 16 | } from "../errors"; |
| 17 | 17 | import * as Effect from "effect/Effect"; |
| 18 | 18 | |
| 19 | −import { LocalSourceArtifactMissingError } from "../runtime/local/errors"; | |
| 19 | +import { LocalSourceArtifactMissingError } from "../runtime/workspace-errors"; | |
| 20 | 20 | import { operationErrors } from "../runtime/policy/operation-errors"; |
| 21 | 21 | import { |
| 22 | 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 | +}; | |