Switch the control plane fully to local file-backed state and delete the old SQL persistence layer. This keeps runtime storage aligned with the local-first architecture and removes the legacy migration path.
Unified · Split
| @@ -161,19 +161,19 @@ Because of that, these surfaces share: · 160 unmodified lines | ||
| 161 | 161 | |
| 162 | 162 | ## Persistence model |
| 163 | 163 | |
| 164 | −The persistence layer is SQL-backed. | |
| 164 | +The persistence layer is local-file-backed. | |
| 165 | 165 | |
| 166 | 166 | Default behavior: |
| 167 | 167 | |
| 168 | −- embedded PGlite for local use | |
| 168 | +- workspace config and state are stored in local files | |
| 169 | 169 | |
| 170 | 170 | Optional behavior: |
| 171 | 171 | |
| 172 | −- Postgres when a Postgres URL is supplied | |
| 172 | +- future cloud backends can plug in behind Effect service boundaries | |
| 173 | 173 | |
| 174 | −At a high level, the database stores these domains: | |
| 174 | +At a high level, the local control plane stores these domains: | |
| 175 | 175 | |
| 176 | −- installation identity: local installation, accounts, organizations, memberships, workspaces | |
| 176 | +- installation identity: local installation identity for the workspace | |
| 177 | 177 | - source state: sources, auth sessions, source credential bindings |
| 178 | 178 | - tool model: tool artifacts and related metadata extracted from sources |
| 179 | 179 | - secret state: credentials and secret materials |
| @@ -31,7 +31,7 @@ We are on v3 of this codebase which is a completely fresh start, the original co · 30 unmodified lines | ||
| 31 | 31 | Architecture I like: |
| 32 | 32 | |
| 33 | 33 | Database: |
| 34 | −Postgres SQLIte for local, Postgres SQL for cloud hosted version | |
| 34 | +Local-file-backed control plane for local use, with room for future hosted backends | |
| 35 | 35 | |
| 36 | 36 | Server: |
| 37 | 37 | |
| @@ -69,7 +69,7 @@ Runtimes: · 31 unmodified lines | ||
| 69 | 69 | Other: |
| 70 | 70 | |
| 71 | 71 | - Avoid hardcoding strings in the core libraries, prefer programming to interfaces, this is because it allows people to build custom adapters without having to modify the core |
| 72 | −- Aim for composability of adapters. For example, the cloud product may be able to leverage storing secrets both encrypted in postgres and also BYO 1password setup. This does not need to be implemented today but worth noting | |
| 72 | +- Aim for composability of adapters. For example, the cloud product may eventually store secrets in a hosted backend while still supporting BYO 1Password. This does not need to be implemented today but is worth noting. | |
| 73 | 73 | - This is a fresh start so we can make whatever changes we need. |
| 74 | 74 | |
| 75 | 75 | Rough architecture is: Turborepo monorepo, effect-vitest for testing, bun for package manager / running apps (but we leverage Effect wherever possible rather than bun's apis i.e for the server), Next.js for web app |
| @@ -280,18 +280,16 @@ The server also maintains local PID and log files in its runtime directory. · 279 unmodified lines | ||
| 280 | 280 | |
| 281 | 281 | ## Persistence and data |
| 282 | 282 | |
| 283 | −`executor` persists the local control plane to SQL. | |
| 284 | − | |
| 285 | −By default it uses embedded PGlite for a local install, which keeps the product self-contained. If you provide a Postgres URL, the persistence layer can target real Postgres instead. | |
| 283 | +`executor` persists the local control plane to local files. | |
| 286 | 284 | |
| 287 | 285 | Persisted concepts include: |
| 288 | 286 | |
| 289 | −- the local installation record | |
| 290 | −- organizations and workspaces provisioned for that install | |
| 287 | +- local installation identity | |
| 291 | 288 | - connected sources |
| 292 | 289 | - indexed tool artifacts and related metadata |
| 293 | 290 | - credentials and secret material bindings |
| 294 | 291 | - source auth sessions |
| 292 | +- execution and interaction state | |
| 295 | 293 | - executions and execution interactions |
| 296 | 294 | - policies |
| 297 | 295 | |
| @@ -324,6 +322,14 @@ If you are exploring the repo, these are the directories that matter most: · 26 unmodified lines | ||
| 324 | 322 | - `packages/executor-mcp`: MCP bridge for `execute` and `resume` |
| 325 | 323 | - `packages/codemode-*`: core tool abstractions plus MCP and OpenAPI adapters |
| 326 | 324 | |
| 325 | +## Releasing | |
| 326 | + | |
| 327 | +- `bun run --cwd apps/executor release:publish` is the only supported publish command. | |
| 328 | +- Stable releases use a normal semver like `1.2.3` and publish to npm under `latest`. | |
| 329 | +- Beta releases use a prerelease semver like `1.3.0-beta.1` and publish to npm under `beta`. | |
| 330 | +- Push a matching git tag such as `v1.2.3` or `v1.3.0-beta.1` to trigger `.github/workflows/publish-executor-package.yml`. | |
| 331 | +- To build and pack the publish artifact locally without publishing, run `bun run --cwd apps/executor release:publish:dry-run`. | |
| 332 | + | |
| 327 | 333 | ## Project status |
| 328 | 334 | |
| 329 | 335 | This repository is explicitly on its third major architecture iteration. |
| @@ -44,7 +44,6 @@ import { · 43 unmodified lines | ||
| 44 | 44 | seedGithubOpenApiSourceInWorkspace, |
| 45 | 45 | } from "./dev"; |
| 46 | 46 | import { |
| 47 | − resolveRuntimeMigrationsDir, | |
| 48 | 47 | resolveRuntimeWebAssetsDir, |
| 49 | 48 | resolveSelfCommand, |
| 50 | 49 | } from "./runtime-paths"; |
| @@ -274,9 +273,7 @@ const buildRunWorkflowText = ( · 223 unmodified lines | ||
| 274 | 273 | |
| 275 | 274 | const loadRunWorkflowText = (): Effect.Effect<string, Error, never> => |
| 276 | 275 | Effect.acquireUseRelease( |
| 277 | − createControlPlaneRuntime({ | |
| 278 | − localDataDir: DEFAULT_LOCAL_DATA_DIR, | |
| 279 | − }).pipe(Effect.mapError(toError)), | |
| 276 | + createControlPlaneRuntime({}).pipe(Effect.mapError(toError)), | |
| 280 | 277 | (runtime) => |
| 281 | 278 | Effect.gen(function* () { |
| 282 | 279 | const environment = yield* Effect.gen(function* () { |
| @@ -414,13 +411,11 @@ const isServerReachable = (baseUrl: string) => · 131 unmodified lines | ||
| 414 | 411 | |
| 415 | 412 | const getDefaultServerOptions = (port: number = DEFAULT_SERVER_PORT) => { |
| 416 | 413 | const assetsDir = resolveRuntimeWebAssetsDir(); |
| 417 | − const migrationsFolder = resolveRuntimeMigrationsDir(); | |
| 418 | 414 | |
| 419 | 415 | return { |
| 420 | 416 | host: DEFAULT_SERVER_HOST, |
| 421 | 417 | port, |
| 422 | 418 | localDataDir: DEFAULT_LOCAL_DATA_DIR, |
| 423 | − migrationsFolder: migrationsFolder ?? undefined, | |
| 424 | 419 | pidFile: DEFAULT_SERVER_PID_FILE, |
| 425 | 420 | ui: assetsDir ? { assetsDir } : undefined, |
| 426 | 421 | }; |
| @@ -533,7 +528,6 @@ type LocalServerStatus = { · 106 unmodified lines | ||
| 533 | 528 | logFile: string; |
| 534 | 529 | localDataDir: string; |
| 535 | 530 | webAssetsDir: string | null; |
| 536 | − migrationsDir: string | null; | |
| 537 | 531 | installation: { |
| 538 | 532 | accountId: string; |
| 539 | 533 | workspaceId: string; |
| @@ -571,7 +565,6 @@ const getServerStatus = (baseUrl: string): Effect.Effect<LocalServerStatus, Erro · 31 unmodified lines | ||
| 571 | 565 | logFile, |
| 572 | 566 | localDataDir: DEFAULT_LOCAL_DATA_DIR, |
| 573 | 567 | webAssetsDir: resolveRuntimeWebAssetsDir(), |
| 574 | − migrationsDir: resolveRuntimeMigrationsDir(), | |
| 575 | 568 | installation, |
| 576 | 569 | denoVersion, |
| 577 | 570 | } satisfies LocalServerStatus; |
| @@ -587,7 +580,6 @@ const renderStatus = (status: LocalServerStatus): string => · 9 unmodified lines | ||
| 587 | 580 | `logFile: ${status.logFile}`, |
| 588 | 581 | `localDataDir: ${status.localDataDir}`, |
| 589 | 582 | `webAssetsDir: ${status.webAssetsDir ?? "missing"}`, |
| 590 | − `migrationsDir: ${status.migrationsDir ?? "missing"}`, | |
| 591 | 583 | `workspaceId: ${status.installation?.workspaceId ?? "unavailable"}`, |
| 592 | 584 | `denoSandbox: ${renderDenoSandboxDetail(status.denoVersion)}`, |
| 593 | 585 | ].join("\n"); |
| @@ -616,10 +608,6 @@ const getDoctorReport = (baseUrl: string) => · 22 unmodified lines | ||
| 616 | 608 | ok: status.webAssetsDir !== null, |
| 617 | 609 | detail: status.webAssetsDir ?? "missing bundled web assets", |
| 618 | 610 | }, |
| 619 | − migrations: { | |
| 620 | − ok: status.migrationsDir !== null, | |
| 621 | − detail: status.migrationsDir ?? "missing migrations directory", | |
| 622 | − }, | |
| 623 | 611 | installation: { |
| 624 | 612 | ok: status.installation !== null, |
| 625 | 613 | detail: status.installation |
| @@ -3,7 +3,6 @@ import { dirname, extname, resolve } from "node:path"; · 2 unmodified lines | ||
| 3 | 3 | import { fileURLToPath } from "node:url"; |
| 4 | 4 | |
| 5 | 5 | import { |
| 6 | − EXECUTOR_MIGRATIONS_DIR_ENV, | |
| 7 | 6 | EXECUTOR_WEB_ASSETS_DIR_ENV, |
| 8 | 7 | } from "@executor/server"; |
| 9 | 8 | |
| @@ -93,20 +92,3 @@ export const resolveRuntimeWebAssetsDir = (): string | null => { · 83 unmodified lines | ||
| 93 | 92 | |
| 94 | 93 | return resolveIfExists(resolve(sourceDir, "../../../web/dist")); |
| 95 | 94 | }; |
| 96 | − | |
| 97 | −export const resolveRuntimeMigrationsDir = (): string | null => { | |
| 98 | − const explicit = resolveIfExists(process.env[EXECUTOR_MIGRATIONS_DIR_ENV]); | |
| 99 | − if (explicit) { | |
| 100 | − return explicit; | |
| 101 | − } | |
| 102 | − | |
| 103 | − const resourcesRoot = resolveRuntimeResourcesRoot(); | |
| 104 | − const bundled = resourcesRoot | |
| 105 | − ? resolveIfExists(resolve(resourcesRoot, "migrations")) | |
| 106 | − : null; | |
| 107 | − if (bundled) { | |
| 108 | − return bundled; | |
| 109 | − } | |
| 110 | − | |
| 111 | − return resolveIfExists(resolve(sourceDir, "../../../../packages/control-plane/drizzle")); | |
| 112 | −}; | |
| @@ -1,13 +0,0 @@ | ||
| 1 | −const databaseUrl = | |
| 2 | − process.env.DATABASE_URL ?? "postgres://localhost:5432/executor_v3"; | |
| 3 | − | |
| 4 | −export default { | |
| 5 | − dialect: "postgresql", | |
| 6 | − schema: "./src/persistence/schema.ts", | |
| 7 | − out: "./drizzle", | |
| 8 | − dbCredentials: { | |
| 9 | − url: databaseUrl, | |
| 10 | − }, | |
| 11 | − strict: true, | |
| 12 | − verbose: true, | |
| 13 | −}; | |
| @@ -1,158 +0,0 @@ | ||
| 1 | −CREATE TABLE "accounts" ( | |
| 2 | − "id" text PRIMARY KEY, | |
| 3 | − "provider" text NOT NULL, | |
| 4 | − "subject" text NOT NULL, | |
| 5 | − "email" text, | |
| 6 | − "display_name" text, | |
| 7 | − "created_at" bigint NOT NULL, | |
| 8 | − "updated_at" bigint NOT NULL, | |
| 9 | − CONSTRAINT "accounts_provider_check" CHECK ("provider" in ('local', 'workos', 'service')) | |
| 10 | −); | |
| 11 | −--> statement-breakpoint | |
| 12 | −CREATE TABLE "execution_interactions" ( | |
| 13 | − "id" text PRIMARY KEY, | |
| 14 | − "execution_id" text NOT NULL, | |
| 15 | − "status" text NOT NULL, | |
| 16 | − "kind" text NOT NULL, | |
| 17 | − "payload_json" text NOT NULL, | |
| 18 | − "response_json" text, | |
| 19 | − "created_at" bigint NOT NULL, | |
| 20 | − "updated_at" bigint NOT NULL, | |
| 21 | − CONSTRAINT "execution_interactions_status_check" CHECK ("status" in ('pending', 'resolved', 'cancelled')) | |
| 22 | −); | |
| 23 | −--> statement-breakpoint | |
| 24 | −CREATE TABLE "executions" ( | |
| 25 | − "id" text PRIMARY KEY, | |
| 26 | − "workspace_id" text NOT NULL, | |
| 27 | − "created_by_account_id" text NOT NULL, | |
| 28 | − "status" text NOT NULL, | |
| 29 | − "code" text NOT NULL, | |
| 30 | − "result_json" text, | |
| 31 | − "error_text" text, | |
| 32 | − "logs_json" text, | |
| 33 | − "started_at" bigint, | |
| 34 | − "completed_at" bigint, | |
| 35 | − "created_at" bigint NOT NULL, | |
| 36 | − "updated_at" bigint NOT NULL, | |
| 37 | − CONSTRAINT "executions_status_check" CHECK ("status" in ('pending', 'running', 'waiting_for_interaction', 'completed', 'failed', 'cancelled')) | |
| 38 | −); | |
| 39 | −--> statement-breakpoint | |
| 40 | −CREATE TABLE "local_installations" ( | |
| 41 | − "id" text PRIMARY KEY, | |
| 42 | − "account_id" text NOT NULL, | |
| 43 | − "organization_id" text NOT NULL, | |
| 44 | − "workspace_id" text NOT NULL, | |
| 45 | − "created_at" bigint NOT NULL, | |
| 46 | − "updated_at" bigint NOT NULL | |
| 47 | −); | |
| 48 | −--> statement-breakpoint | |
| 49 | −CREATE TABLE "organization_memberships" ( | |
| 50 | − "id" text PRIMARY KEY, | |
| 51 | − "organization_id" text NOT NULL, | |
| 52 | − "account_id" text NOT NULL, | |
| 53 | − "role" text NOT NULL, | |
| 54 | − "status" text NOT NULL, | |
| 55 | − "billable" boolean NOT NULL, | |
| 56 | − "invited_by_account_id" text, | |
| 57 | − "joined_at" bigint, | |
| 58 | − "created_at" bigint NOT NULL, | |
| 59 | − "updated_at" bigint NOT NULL, | |
| 60 | − CONSTRAINT "organization_memberships_role_check" CHECK ("role" in ('viewer', 'editor', 'admin', 'owner')), | |
| 61 | − CONSTRAINT "organization_memberships_status_check" CHECK ("status" in ('invited', 'active', 'suspended', 'removed')) | |
| 62 | −); | |
| 63 | −--> statement-breakpoint | |
| 64 | −CREATE TABLE "organizations" ( | |
| 65 | − "id" text PRIMARY KEY, | |
| 66 | − "slug" text NOT NULL, | |
| 67 | − "name" text NOT NULL, | |
| 68 | − "status" text NOT NULL, | |
| 69 | − "created_by_account_id" text, | |
| 70 | − "created_at" bigint NOT NULL, | |
| 71 | − "updated_at" bigint NOT NULL, | |
| 72 | − CONSTRAINT "organizations_status_check" CHECK ("status" in ('active', 'suspended', 'archived')) | |
| 73 | −); | |
| 74 | −--> statement-breakpoint | |
| 75 | −CREATE TABLE "policies" ( | |
| 76 | − "id" text PRIMARY KEY, | |
| 77 | − "workspace_id" text NOT NULL, | |
| 78 | − "target_account_id" text, | |
| 79 | − "client_id" text, | |
| 80 | − "resource_type" text NOT NULL, | |
| 81 | − "resource_pattern" text NOT NULL, | |
| 82 | − "match_type" text NOT NULL, | |
| 83 | − "effect" text NOT NULL, | |
| 84 | − "approval_mode" text NOT NULL, | |
| 85 | − "argument_conditions_json" text, | |
| 86 | − "priority" bigint NOT NULL, | |
| 87 | − "enabled" boolean NOT NULL, | |
| 88 | − "created_at" bigint NOT NULL, | |
| 89 | − "updated_at" bigint NOT NULL, | |
| 90 | − CONSTRAINT "policies_resource_type_check" CHECK ("resource_type" in ('all_tools', 'source', 'namespace', 'tool_path')), | |
| 91 | − CONSTRAINT "policies_match_type_check" CHECK ("match_type" in ('glob', 'exact')), | |
| 92 | − CONSTRAINT "policies_effect_check" CHECK ("effect" in ('allow', 'deny')), | |
| 93 | − CONSTRAINT "policies_approval_mode_check" CHECK ("approval_mode" in ('auto', 'required')) | |
| 94 | −); | |
| 95 | −--> statement-breakpoint | |
| 96 | −CREATE TABLE "source_credential_bindings" ( | |
| 97 | − "workspace_id" text, | |
| 98 | − "source_id" text, | |
| 99 | − "token_provider_id" text, | |
| 100 | − "token_handle" text, | |
| 101 | − "refresh_token_provider_id" text, | |
| 102 | − "refresh_token_handle" text, | |
| 103 | − "created_at" bigint NOT NULL, | |
| 104 | − "updated_at" bigint NOT NULL, | |
| 105 | − CONSTRAINT "source_credential_bindings_pkey" PRIMARY KEY("workspace_id","source_id") | |
| 106 | −); | |
| 107 | −--> statement-breakpoint | |
| 108 | −CREATE TABLE "sources" ( | |
| 109 | − "workspace_id" text, | |
| 110 | − "source_id" text, | |
| 111 | − "name" text NOT NULL, | |
| 112 | − "kind" text NOT NULL, | |
| 113 | − "endpoint" text NOT NULL, | |
| 114 | − "status" text NOT NULL, | |
| 115 | − "enabled" boolean NOT NULL, | |
| 116 | − "namespace" text, | |
| 117 | − "transport" text, | |
| 118 | − "query_params_json" text, | |
| 119 | − "headers_json" text, | |
| 120 | − "spec_url" text, | |
| 121 | − "default_headers_json" text, | |
| 122 | − "auth_kind" text NOT NULL, | |
| 123 | − "auth_header_name" text, | |
| 124 | − "auth_prefix" text, | |
| 125 | − "source_hash" text, | |
| 126 | − "last_error" text, | |
| 127 | − "created_at" bigint NOT NULL, | |
| 128 | − "updated_at" bigint NOT NULL, | |
| 129 | − CONSTRAINT "sources_pkey" PRIMARY KEY("workspace_id","source_id"), | |
| 130 | − CONSTRAINT "sources_kind_check" CHECK ("kind" in ('mcp', 'openapi', 'graphql', 'internal')), | |
| 131 | − CONSTRAINT "sources_status_check" CHECK ("status" in ('draft', 'probing', 'auth_required', 'connected', 'error')), | |
| 132 | − CONSTRAINT "sources_transport_check" CHECK ("transport" is null or "transport" in ('auto', 'streamable-http', 'sse')), | |
| 133 | − CONSTRAINT "sources_auth_kind_check" CHECK ("auth_kind" in ('none', 'bearer', 'oauth2')) | |
| 134 | −); | |
| 135 | −--> statement-breakpoint | |
| 136 | −CREATE TABLE "workspaces" ( | |
| 137 | − "id" text PRIMARY KEY, | |
| 138 | − "organization_id" text NOT NULL, | |
| 139 | − "name" text NOT NULL, | |
| 140 | − "created_by_account_id" text, | |
| 141 | − "created_at" bigint NOT NULL, | |
| 142 | − "updated_at" bigint NOT NULL | |
| 143 | −); | |
| 144 | −--> statement-breakpoint | |
| 145 | −CREATE UNIQUE INDEX "accounts_provider_subject_idx" ON "accounts" ("provider","subject");--> statement-breakpoint | |
| 146 | −CREATE INDEX "accounts_updated_idx" ON "accounts" ("updated_at","id");--> statement-breakpoint | |
| 147 | −CREATE INDEX "execution_interactions_execution_idx" ON "execution_interactions" ("execution_id","updated_at","id");--> statement-breakpoint | |
| 148 | −CREATE INDEX "executions_workspace_idx" ON "executions" ("workspace_id","updated_at","id");--> statement-breakpoint | |
| 149 | −CREATE INDEX "organization_memberships_org_idx" ON "organization_memberships" ("organization_id");--> statement-breakpoint | |
| 150 | −CREATE INDEX "organization_memberships_account_idx" ON "organization_memberships" ("account_id");--> statement-breakpoint | |
| 151 | −CREATE UNIQUE INDEX "organization_memberships_org_account_idx" ON "organization_memberships" ("organization_id","account_id");--> statement-breakpoint | |
| 152 | −CREATE UNIQUE INDEX "organizations_slug_idx" ON "organizations" ("slug");--> statement-breakpoint | |
| 153 | −CREATE INDEX "organizations_updated_idx" ON "organizations" ("updated_at","id");--> statement-breakpoint | |
| 154 | −CREATE INDEX "policies_workspace_idx" ON "policies" ("workspace_id","updated_at","id");--> statement-breakpoint | |
| 155 | −CREATE INDEX "source_credential_bindings_workspace_idx" ON "source_credential_bindings" ("workspace_id","updated_at","source_id");--> statement-breakpoint | |
| 156 | −CREATE UNIQUE INDEX "sources_workspace_name_idx" ON "sources" ("workspace_id","name");--> statement-breakpoint | |
| 157 | −CREATE INDEX "workspaces_org_idx" ON "workspaces" ("organization_id");--> statement-breakpoint | |
| 158 | −CREATE UNIQUE INDEX "workspaces_org_name_idx" ON "workspaces" ("organization_id","name"); | |
| @@ -1,1937 +0,0 @@ | ||
| 1 | −{ | |
| 2 | − "version": "8", | |
| 3 | − "dialect": "postgres", | |
| 4 | − "id": "bac1651d-7a30-4187-a7e2-74bc2b099486", | |
| 5 | − "prevIds": [ | |
| 6 | − "00000000-0000-0000-0000-000000000000" | |
| 7 | − ], | |
| 8 | − "ddl": [ | |
| 9 | − { | |
| 10 | − "isRlsEnabled": false, | |
| 11 | − "name": "accounts", | |
| 12 | − "entityType": "tables", | |
| 13 | − "schema": "public" | |
| 14 | − }, | |
| 15 | − { | |
| 16 | − "isRlsEnabled": false, | |
| 17 | − "name": "execution_interactions", | |
| 18 | − "entityType": "tables", | |
| 19 | − "schema": "public" | |
| 20 | − }, | |
| 21 | − { | |
| 22 | − "isRlsEnabled": false, | |
| 23 | − "name": "executions", | |
| 24 | − "entityType": "tables", | |
| 25 | − "schema": "public" | |
| 26 | − }, | |
| 27 | − { | |
| 28 | − "isRlsEnabled": false, | |
| 29 | − "name": "local_installations", | |
| 30 | − "entityType": "tables", | |
| 31 | − "schema": "public" | |
| 32 | − }, | |
| 33 | − { | |
| 34 | − "isRlsEnabled": false, | |
| 35 | − "name": "organization_memberships", | |
| 36 | − "entityType": "tables", | |
| 37 | − "schema": "public" | |
| 38 | − }, | |
| 39 | − { | |
| 40 | − "isRlsEnabled": false, | |
| 41 | − "name": "organizations", | |
| 42 | − "entityType": "tables", | |
| 43 | − "schema": "public" | |
| 44 | − }, | |
| 45 | − { | |
| 46 | − "isRlsEnabled": false, | |
| 47 | − "name": "policies", | |
| 48 | − "entityType": "tables", | |
| 49 | − "schema": "public" | |
| 50 | − }, | |
| 51 | − { | |
| 52 | − "isRlsEnabled": false, | |
| 53 | − "name": "source_credential_bindings", | |
| 54 | − "entityType": "tables", | |
| 55 | − "schema": "public" | |
| 56 | − }, | |
| 57 | − { | |
| 58 | − "isRlsEnabled": false, | |
| 59 | − "name": "sources", | |
| 60 | − "entityType": "tables", | |
| 61 | − "schema": "public" | |
| 62 | − }, | |
| 63 | − { | |
| 64 | − "isRlsEnabled": false, | |
| 65 | − "name": "workspaces", | |
| 66 | − "entityType": "tables", | |
| 67 | − "schema": "public" | |
| 68 | − }, | |
| 69 | − { | |
| 70 | − "type": "text", | |
| 71 | − "typeSchema": null, | |
| 72 | − "notNull": true, | |
| 73 | − "dimensions": 0, | |
| 74 | − "default": null, | |
| 75 | − "generated": null, | |
| 76 | − "identity": null, | |
| 77 | − "name": "id", | |
| 78 | − "entityType": "columns", | |
| 79 | − "schema": "public", | |
| 80 | − "table": "accounts" | |
| 81 | − }, | |
| 82 | − { | |
| 83 | − "type": "text", | |
| 84 | − "typeSchema": null, | |
| 85 | − "notNull": true, | |
| 86 | − "dimensions": 0, | |
| 87 | − "default": null, | |
| 88 | − "generated": null, | |
| 89 | − "identity": null, | |
| 90 | − "name": "provider", | |
| 91 | − "entityType": "columns", | |
| 92 | − "schema": "public", | |
| 93 | − "table": "accounts" | |
| 94 | − }, | |
| 95 | − { | |
| 96 | − "type": "text", | |
| 97 | − "typeSchema": null, | |
| 98 | − "notNull": true, | |
| 99 | − "dimensions": 0, | |
| 100 | − "default": null, | |
| 101 | − "generated": null, | |
| 102 | − "identity": null, | |
| 103 | − "name": "subject", | |
| 104 | − "entityType": "columns", | |
| 105 | − "schema": "public", | |
| 106 | − "table": "accounts" | |
| 107 | − }, | |
| 108 | − { | |
| 109 | − "type": "text", | |
| 110 | − "typeSchema": null, | |
| 111 | − "notNull": false, | |
| 112 | − "dimensions": 0, | |
| 113 | − "default": null, | |
| 114 | − "generated": null, | |
| 115 | − "identity": null, | |
| 116 | − "name": "email", | |
| 117 | − "entityType": "columns", | |
| 118 | − "schema": "public", | |
| 119 | − "table": "accounts" | |
| 120 | − }, | |
| 121 | − { | |
| 122 | − "type": "text", | |
| 123 | − "typeSchema": null, | |
| 124 | − "notNull": false, | |
| 125 | − "dimensions": 0, | |
| 126 | − "default": null, | |
| 127 | − "generated": null, | |
| 128 | − "identity": null, | |
| 129 | − "name": "display_name", | |
| 130 | − "entityType": "columns", | |
| 131 | − "schema": "public", | |
| 132 | − "table": "accounts" | |
| 133 | − }, | |
| 134 | − { | |
| 135 | − "type": "bigint", | |
| 136 | − "typeSchema": null, | |
| 137 | − "notNull": true, | |
| 138 | − "dimensions": 0, | |
| 139 | − "default": null, | |
| 140 | − "generated": null, | |
| 141 | − "identity": null, | |
| 142 | − "name": "created_at", | |
| 143 | − "entityType": "columns", | |
| 144 | − "schema": "public", | |
| 145 | − "table": "accounts" | |
| 146 | − }, | |
| 147 | − { | |
| 148 | − "type": "bigint", | |
| 149 | − "typeSchema": null, | |
| 150 | − "notNull": true, | |
| 151 | − "dimensions": 0, | |
| 152 | − "default": null, | |
| 153 | − "generated": null, | |
| 154 | − "identity": null, | |
| 155 | − "name": "updated_at", | |
| 156 | − "entityType": "columns", | |
| 157 | − "schema": "public", | |
| 158 | − "table": "accounts" | |
| 159 | − }, | |
| 160 | − { | |
| 161 | − "type": "text", | |
| 162 | − "typeSchema": null, | |
| 163 | − "notNull": true, | |
| 164 | − "dimensions": 0, | |
| 165 | − "default": null, | |
| 166 | − "generated": null, | |
| 167 | − "identity": null, | |
| 168 | − "name": "id", | |
| 169 | − "entityType": "columns", | |
| 170 | − "schema": "public", | |
| 171 | − "table": "execution_interactions" | |
| 172 | − }, | |
| 173 | − { | |
| 174 | − "type": "text", | |
| 175 | − "typeSchema": null, | |
| 176 | − "notNull": true, | |
| 177 | − "dimensions": 0, | |
| 178 | − "default": null, | |
| 179 | − "generated": null, | |
| 180 | − "identity": null, | |
| 181 | − "name": "execution_id", | |
| 182 | − "entityType": "columns", | |
| 183 | − "schema": "public", | |
| 184 | − "table": "execution_interactions" | |
| 185 | − }, | |
| 186 | − { | |
| 187 | − "type": "text", | |
| 188 | − "typeSchema": null, | |
| 189 | − "notNull": true, | |
| 190 | − "dimensions": 0, | |
| 191 | − "default": null, | |
| 192 | − "generated": null, | |
| 193 | − "identity": null, | |
| 194 | − "name": "status", | |
| 195 | − "entityType": "columns", | |
| 196 | − "schema": "public", | |
| 197 | − "table": "execution_interactions" | |
| 198 | − }, | |
| 199 | − { | |
| 200 | − "type": "text", | |
| 201 | − "typeSchema": null, | |
| 202 | − "notNull": true, | |
| 203 | − "dimensions": 0, | |
| 204 | − "default": null, | |
| 205 | − "generated": null, | |
| 206 | − "identity": null, | |
| 207 | − "name": "kind", | |
| 208 | − "entityType": "columns", | |
| 209 | − "schema": "public", | |
| 210 | − "table": "execution_interactions" | |
| 211 | − }, | |
| 212 | − { | |
| 213 | − "type": "text", | |
| 214 | − "typeSchema": null, | |
| 215 | − "notNull": true, | |
| 216 | − "dimensions": 0, | |
| 217 | − "default": null, | |
| 218 | − "generated": null, | |
| 219 | − "identity": null, | |
| 220 | − "name": "payload_json", | |
| 221 | − "entityType": "columns", | |
| 222 | − "schema": "public", | |
| 223 | − "table": "execution_interactions" | |
| 224 | − }, | |
| 225 | − { | |
| 226 | − "type": "text", | |
| 227 | − "typeSchema": null, | |
| 228 | − "notNull": false, | |
| 229 | − "dimensions": 0, | |
| 230 | − "default": null, | |
| 231 | − "generated": null, | |
| 232 | − "identity": null, | |
| 233 | − "name": "response_json", | |
| 234 | − "entityType": "columns", | |
| 235 | − "schema": "public", | |
| 236 | − "table": "execution_interactions" | |
| 237 | − }, | |
| 238 | − { | |
| 239 | − "type": "bigint", | |
| 240 | − "typeSchema": null, | |
| 241 | − "notNull": true, | |
| 242 | − "dimensions": 0, | |
| 243 | − "default": null, | |
| 244 | − "generated": null, | |
| 245 | − "identity": null, | |
| 246 | − "name": "created_at", | |
| 247 | − "entityType": "columns", | |
| 248 | − "schema": "public", | |
| 249 | − "table": "execution_interactions" | |
| 250 | − }, | |
| 251 | − { | |
| 252 | − "type": "bigint", | |
| 253 | − "typeSchema": null, | |
| 254 | − "notNull": true, | |
| 255 | − "dimensions": 0, | |
| 256 | − "default": null, | |
| 257 | − "generated": null, | |
| 258 | − "identity": null, | |
| 259 | − "name": "updated_at", | |
| 260 | − "entityType": "columns", | |
| 261 | − "schema": "public", | |
| 262 | − "table": "execution_interactions" | |
| 263 | − }, | |
| 264 | − { | |
| 265 | − "type": "text", | |
| 266 | − "typeSchema": null, | |
| 267 | − "notNull": true, | |
| 268 | − "dimensions": 0, | |
| 269 | − "default": null, | |
| 270 | − "generated": null, | |
| 271 | − "identity": null, | |
| 272 | − "name": "id", | |
| 273 | − "entityType": "columns", | |
| 274 | − "schema": "public", | |
| 275 | − "table": "executions" | |
| 276 | − }, | |
| 277 | − { | |
| 278 | − "type": "text", | |
| 279 | − "typeSchema": null, | |
| 280 | − "notNull": true, | |
| 281 | − "dimensions": 0, | |
| 282 | − "default": null, | |
| 283 | − "generated": null, | |
| 284 | − "identity": null, | |
| 285 | − "name": "workspace_id", | |
| 286 | − "entityType": "columns", | |
| 287 | − "schema": "public", | |
| 288 | − "table": "executions" | |
| 289 | − }, | |
| 290 | − { | |
| 291 | − "type": "text", | |
| 292 | − "typeSchema": null, | |
| 293 | − "notNull": true, | |
| 294 | − "dimensions": 0, | |
| 295 | − "default": null, | |
| 296 | − "generated": null, | |
| 297 | − "identity": null, | |
| 298 | − "name": "created_by_account_id", | |
| 299 | − "entityType": "columns", | |
| 300 | − "schema": "public", | |
| 301 | − "table": "executions" | |
| 302 | − }, | |
| 303 | − { | |
| 304 | − "type": "text", | |
| 305 | − "typeSchema": null, | |
| 306 | − "notNull": true, | |
| 307 | − "dimensions": 0, | |
| 308 | − "default": null, | |
| 309 | − "generated": null, | |
| 310 | − "identity": null, | |
| 311 | − "name": "status", | |
| 312 | − "entityType": "columns", | |
| 313 | − "schema": "public", | |
| 314 | − "table": "executions" | |
| 315 | − }, | |
| 316 | − { | |
| 317 | − "type": "text", | |
| 318 | − "typeSchema": null, | |
| 319 | − "notNull": true, | |
| 320 | − "dimensions": 0, | |
| 321 | − "default": null, | |
| 322 | − "generated": null, | |
| 323 | − "identity": null, | |
| 324 | − "name": "code", | |
| 325 | − "entityType": "columns", | |
| 326 | − "schema": "public", | |
| 327 | − "table": "executions" | |
| 328 | − }, | |
| 329 | − { | |
| 330 | − "type": "text", | |
| 331 | − "typeSchema": null, | |
| 332 | − "notNull": false, | |
| 333 | − "dimensions": 0, | |
| 334 | − "default": null, | |
| 335 | − "generated": null, | |
| 336 | − "identity": null, | |
| 337 | − "name": "result_json", | |
| 338 | − "entityType": "columns", | |
| 339 | − "schema": "public", | |
| 340 | − "table": "executions" | |
| 341 | − }, | |
| 342 | − { | |
| 343 | − "type": "text", | |
| 344 | − "typeSchema": null, | |
| 345 | − "notNull": false, | |
| 346 | − "dimensions": 0, | |
| 347 | − "default": null, | |
| 348 | − "generated": null, | |
| 349 | − "identity": null, | |
| 350 | − "name": "error_text", | |
| 351 | − "entityType": "columns", | |
| 352 | − "schema": "public", | |
| 353 | − "table": "executions" | |
| 354 | − }, | |
| 355 | − { | |
| 356 | − "type": "text", | |
| 357 | − "typeSchema": null, | |
| 358 | − "notNull": false, | |
| 359 | − "dimensions": 0, | |
| 360 | − "default": null, | |
| 361 | − "generated": null, | |
| 362 | − "identity": null, | |
| 363 | − "name": "logs_json", | |
| 364 | − "entityType": "columns", | |
| 365 | − "schema": "public", | |
| 366 | − "table": "executions" | |
| 367 | − }, | |
| 368 | − { | |
| 369 | − "type": "bigint", | |
| 370 | − "typeSchema": null, | |
| 371 | − "notNull": false, | |
| 372 | − "dimensions": 0, | |
| 373 | − "default": null, | |
| 374 | − "generated": null, | |
| 375 | − "identity": null, | |
| 376 | − "name": "started_at", | |
| 377 | − "entityType": "columns", | |
| 378 | − "schema": "public", | |
| 379 | − "table": "executions" | |
| 380 | − }, | |
| 381 | − { | |
| 382 | − "type": "bigint", | |
| 383 | − "typeSchema": null, | |
| 384 | − "notNull": false, | |
| 385 | − "dimensions": 0, | |
| 386 | − "default": null, | |
| 387 | − "generated": null, | |
| 388 | − "identity": null, | |
| 389 | − "name": "completed_at", | |
| 390 | − "entityType": "columns", | |
| 391 | − "schema": "public", | |
| 392 | − "table": "executions" | |
| 393 | − }, | |
| 394 | − { | |
| 395 | − "type": "bigint", | |
| 396 | − "typeSchema": null, | |
| 397 | − "notNull": true, | |
| 398 | − "dimensions": 0, | |
| 399 | − "default": null, | |
| 400 | − "generated": null, | |
| 401 | − "identity": null, | |
| 402 | − "name": "created_at", | |
| 403 | − "entityType": "columns", | |
| 404 | − "schema": "public", | |
| 405 | − "table": "executions" | |
| 406 | − }, | |
| 407 | − { | |
| 408 | − "type": "bigint", | |
| 409 | − "typeSchema": null, | |
| 410 | − "notNull": true, | |
| 411 | − "dimensions": 0, | |
| 412 | − "default": null, | |
| 413 | − "generated": null, | |
| 414 | − "identity": null, | |
| 415 | − "name": "updated_at", | |
| 416 | − "entityType": "columns", | |
| 417 | − "schema": "public", | |
| 418 | − "table": "executions" | |
| 419 | − }, | |
| 420 | − { | |
| 421 | − "type": "text", | |
| 422 | − "typeSchema": null, | |
| 423 | − "notNull": true, | |
| 424 | − "dimensions": 0, | |
| 425 | − "default": null, | |
| 426 | − "generated": null, | |
| 427 | − "identity": null, | |
| 428 | − "name": "id", | |
| 429 | − "entityType": "columns", | |
| 430 | − "schema": "public", | |
| 431 | − "table": "local_installations" | |
| 432 | − }, | |
| 433 | − { | |
| 434 | − "type": "text", | |
| 435 | − "typeSchema": null, | |
| 436 | − "notNull": true, | |
| 437 | − "dimensions": 0, | |
| 438 | − "default": null, | |
| 439 | − "generated": null, | |
| 440 | − "identity": null, | |
| 441 | − "name": "account_id", | |
| 442 | − "entityType": "columns", | |
| 443 | − "schema": "public", | |
| 444 | − "table": "local_installations" | |
| 445 | − }, | |
| 446 | − { | |
| 447 | − "type": "text", | |
| 448 | − "typeSchema": null, | |
| 449 | − "notNull": true, | |
| 450 | − "dimensions": 0, | |
| 451 | − "default": null, | |
| 452 | − "generated": null, | |
| 453 | − "identity": null, | |
| 454 | − "name": "organization_id", | |
| 455 | − "entityType": "columns", | |
| 456 | − "schema": "public", | |
| 457 | − "table": "local_installations" | |
| 458 | − }, | |
| 459 | − { | |
| 460 | − "type": "text", | |
| 461 | − "typeSchema": null, | |
| 462 | − "notNull": true, | |
| 463 | − "dimensions": 0, | |
| 464 | − "default": null, | |
| 465 | − "generated": null, | |
| 466 | − "identity": null, | |
| 467 | − "name": "workspace_id", | |
| 468 | − "entityType": "columns", | |
| 469 | − "schema": "public", | |
| 470 | − "table": "local_installations" | |
| 471 | − }, | |
| 472 | − { | |
| 473 | − "type": "bigint", | |
| 474 | − "typeSchema": null, | |
| 475 | − "notNull": true, | |
| 476 | − "dimensions": 0, | |
| 477 | − "default": null, | |
| 478 | − "generated": null, | |
| 479 | − "identity": null, | |
| 480 | − "name": "created_at", | |
| 481 | − "entityType": "columns", | |
| 482 | − "schema": "public", | |
| 483 | − "table": "local_installations" | |
| 484 | − }, | |
| 485 | − { | |
| 486 | − "type": "bigint", | |
| 487 | − "typeSchema": null, | |
| 488 | − "notNull": true, | |
| 489 | − "dimensions": 0, | |
| 490 | − "default": null, | |
| 491 | − "generated": null, | |
| 492 | − "identity": null, | |
| 493 | − "name": "updated_at", | |
| 494 | − "entityType": "columns", | |
| 495 | − "schema": "public", | |
| 496 | − "table": "local_installations" | |
| 497 | − }, | |
| 498 | − { | |
| 499 | − "type": "text", | |
| 500 | − "typeSchema": null, | |
| 501 | − "notNull": true, | |
| 502 | − "dimensions": 0, | |
| 503 | − "default": null, | |
| 504 | − "generated": null, | |
| 505 | − "identity": null, | |
| 506 | − "name": "id", | |
| 507 | − "entityType": "columns", | |
| 508 | − "schema": "public", | |
| 509 | − "table": "organization_memberships" | |
| 510 | − }, | |
| 511 | − { | |
| 512 | − "type": "text", | |
| 513 | − "typeSchema": null, | |
| 514 | − "notNull": true, | |
| 515 | − "dimensions": 0, | |
| 516 | − "default": null, | |
| 517 | − "generated": null, | |
| 518 | − "identity": null, | |
| 519 | − "name": "organization_id", | |
| 520 | − "entityType": "columns", | |
| 521 | − "schema": "public", | |
| 522 | − "table": "organization_memberships" | |
| 523 | − }, | |
| 524 | − { | |
| 525 | − "type": "text", | |
| 526 | − "typeSchema": null, | |
| 527 | − "notNull": true, | |
| 528 | − "dimensions": 0, | |
| 529 | − "default": null, | |
| 530 | − "generated": null, | |
| 531 | − "identity": null, | |
| 532 | − "name": "account_id", | |
| 533 | − "entityType": "columns", | |
| 534 | − "schema": "public", | |
| 535 | − "table": "organization_memberships" | |
| 536 | − }, | |
| 537 | − { | |
| 538 | − "type": "text", | |
| 539 | − "typeSchema": null, | |
| 540 | − "notNull": true, | |
| 541 | − "dimensions": 0, | |
| 542 | − "default": null, | |
| 543 | − "generated": null, | |
| 544 | − "identity": null, | |
| 545 | − "name": "role", | |
| 546 | − "entityType": "columns", | |
| 547 | − "schema": "public", | |
| 548 | − "table": "organization_memberships" | |
| 549 | − }, | |
| 550 | − { | |
| 551 | − "type": "text", | |
| 552 | − "typeSchema": null, | |
| 553 | − "notNull": true, | |
| 554 | − "dimensions": 0, | |
| 555 | − "default": null, | |
| 556 | − "generated": null, | |
| 557 | − "identity": null, | |
| 558 | − "name": "status", | |
| 559 | − "entityType": "columns", | |
| 560 | − "schema": "public", | |
| 561 | − "table": "organization_memberships" | |
| 562 | − }, | |
| 563 | − { | |
| 564 | − "type": "boolean", | |
| 565 | − "typeSchema": null, | |
| 566 | − "notNull": true, | |
| 567 | − "dimensions": 0, | |
| 568 | − "default": null, | |
| 569 | − "generated": null, | |
| 570 | − "identity": null, | |
| 571 | − "name": "billable", | |
| 572 | − "entityType": "columns", | |
| 573 | − "schema": "public", | |
| 574 | − "table": "organization_memberships" | |
| 575 | − }, | |
| 576 | − { | |
| 577 | − "type": "text", | |
| 578 | − "typeSchema": null, | |
| 579 | − "notNull": false, | |
| 580 | − "dimensions": 0, | |
| 581 | − "default": null, | |
| 582 | − "generated": null, | |
| 583 | − "identity": null, | |
| 584 | − "name": "invited_by_account_id", | |
| 585 | − "entityType": "columns", | |
| 586 | − "schema": "public", | |
| 587 | − "table": "organization_memberships" | |
| 588 | − }, | |
| 589 | − { | |
| 590 | − "type": "bigint", | |
| 591 | − "typeSchema": null, | |
| 592 | − "notNull": false, | |
| 593 | − "dimensions": 0, | |
| 594 | − "default": null, | |
| 595 | − "generated": null, | |
| 596 | − "identity": null, | |
| 597 | − "name": "joined_at", | |
| 598 | − "entityType": "columns", | |
| 599 | − "schema": "public", | |
| 600 | − "table": "organization_memberships" | |
| 601 | − }, | |
| 602 | − { | |
| 603 | − "type": "bigint", | |
| 604 | − "typeSchema": null, | |
| 605 | − "notNull": true, | |
| 606 | − "dimensions": 0, | |
| 607 | − "default": null, | |
| 608 | − "generated": null, | |
| 609 | − "identity": null, | |
| 610 | − "name": "created_at", | |
| 611 | − "entityType": "columns", | |
| 612 | − "schema": "public", | |
| 613 | − "table": "organization_memberships" | |
| 614 | − }, | |
| 615 | − { | |
| 616 | − "type": "bigint", | |
| 617 | − "typeSchema": null, | |
| 618 | − "notNull": true, | |
| 619 | − "dimensions": 0, | |
| 620 | − "default": null, | |
| 621 | − "generated": null, | |
| 622 | − "identity": null, | |
| 623 | − "name": "updated_at", | |
| 624 | − "entityType": "columns", | |
| 625 | − "schema": "public", | |
| 626 | − "table": "organization_memberships" | |
| 627 | − }, | |
| 628 | − { | |
| 629 | − "type": "text", | |
| 630 | − "typeSchema": null, | |
| 631 | − "notNull": true, | |
| 632 | − "dimensions": 0, | |
| 633 | − "default": null, | |
| 634 | − "generated": null, | |
| 635 | − "identity": null, | |
| 636 | − "name": "id", | |
| 637 | − "entityType": "columns", | |
| 638 | − "schema": "public", | |
| 639 | − "table": "organizations" | |
| 640 | − }, | |
| 641 | − { | |
| 642 | − "type": "text", | |
| 643 | − "typeSchema": null, | |
| 644 | − "notNull": true, | |
| 645 | − "dimensions": 0, | |
| 646 | − "default": null, | |
| 647 | − "generated": null, | |
| 648 | − "identity": null, | |
| 649 | − "name": "slug", | |
| 650 | − "entityType": "columns", | |
| 651 | − "schema": "public", | |
| 652 | − "table": "organizations" | |
| 653 | − }, | |
| 654 | − { | |
| 655 | − "type": "text", | |
| 656 | − "typeSchema": null, | |
| 657 | − "notNull": true, | |
| 658 | − "dimensions": 0, | |
| 659 | − "default": null, | |
| 660 | − "generated": null, | |
| 661 | − "identity": null, | |
| 662 | − "name": "name", | |
| 663 | − "entityType": "columns", | |
| 664 | − "schema": "public", | |
| 665 | − "table": "organizations" | |
| 666 | − }, | |
| 667 | − { | |
| 668 | − "type": "text", | |
| 669 | − "typeSchema": null, | |
| 670 | − "notNull": true, | |
| 671 | − "dimensions": 0, | |
| 672 | − "default": null, | |
| 673 | − "generated": null, | |
| 674 | − "identity": null, | |
| 675 | − "name": "status", | |
| 676 | − "entityType": "columns", | |
| 677 | − "schema": "public", | |
| 678 | − "table": "organizations" | |
| 679 | − }, | |
| 680 | − { | |
| 681 | − "type": "text", | |
| 682 | − "typeSchema": null, | |
| 683 | − "notNull": false, | |
| 684 | − "dimensions": 0, | |
| 685 | − "default": null, | |
| 686 | − "generated": null, | |
| 687 | − "identity": null, | |
| 688 | − "name": "created_by_account_id", | |
| 689 | − "entityType": "columns", | |
| 690 | − "schema": "public", | |
| 691 | − "table": "organizations" | |
| 692 | − }, | |
| 693 | − { | |
| 694 | − "type": "bigint", | |
| 695 | − "typeSchema": null, | |
| 696 | − "notNull": true, | |
| 697 | − "dimensions": 0, | |
| 698 | − "default": null, | |
| 699 | − "generated": null, | |
| 700 | − "identity": null, | |
| 701 | − "name": "created_at", | |
| 702 | − "entityType": "columns", | |
| 703 | − "schema": "public", | |
| 704 | − "table": "organizations" | |
| 705 | − }, | |
| 706 | − { | |
| 707 | − "type": "bigint", | |
| 708 | − "typeSchema": null, | |
| 709 | − "notNull": true, | |
| 710 | − "dimensions": 0, | |
| 711 | − "default": null, | |
| 712 | − "generated": null, | |
| 713 | − "identity": null, | |
| 714 | − "name": "updated_at", | |
| 715 | − "entityType": "columns", | |
| 716 | − "schema": "public", | |
| 717 | − "table": "organizations" | |
| 718 | − }, | |
| 719 | − { | |
| 720 | − "type": "text", | |
| 721 | − "typeSchema": null, | |
| 722 | − "notNull": true, | |
| 723 | − "dimensions": 0, | |
| 724 | − "default": null, | |
| 725 | − "generated": null, | |
| 726 | − "identity": null, | |
| 727 | − "name": "id", | |
| 728 | − "entityType": "columns", | |
| 729 | − "schema": "public", | |
| 730 | − "table": "policies" | |
| 731 | − }, | |
| 732 | − { | |
| 733 | − "type": "text", | |
| 734 | − "typeSchema": null, | |
| 735 | − "notNull": true, | |
| 736 | − "dimensions": 0, | |
| 737 | − "default": null, | |
| 738 | − "generated": null, | |
| 739 | − "identity": null, | |
| 740 | − "name": "workspace_id", | |
| 741 | − "entityType": "columns", | |
| 742 | − "schema": "public", | |
| 743 | − "table": "policies" | |
| 744 | − }, | |
| 745 | − { | |
| 746 | − "type": "text", | |
| 747 | − "typeSchema": null, | |
| 748 | − "notNull": false, | |
| 749 | − "dimensions": 0, | |
| 750 | − "default": null, | |
| 751 | − "generated": null, | |
| 752 | − "identity": null, | |
| 753 | − "name": "target_account_id", | |
| 754 | − "entityType": "columns", | |
| 755 | − "schema": "public", | |
| 756 | − "table": "policies" | |
| 757 | − }, | |
| 758 | − { | |
| 759 | − "type": "text", | |
| 760 | − "typeSchema": null, | |
| 761 | − "notNull": false, | |
| 762 | − "dimensions": 0, | |
| 763 | − "default": null, | |
| 764 | − "generated": null, | |
| 765 | − "identity": null, | |
| 766 | − "name": "client_id", | |
| 767 | − "entityType": "columns", | |
| 768 | − "schema": "public", | |
| 769 | − "table": "policies" | |
| 770 | − }, | |
| 771 | − { | |
| 772 | − "type": "text", | |
| 773 | − "typeSchema": null, | |
| 774 | − "notNull": true, | |
| 775 | − "dimensions": 0, | |
| 776 | − "default": null, | |
| 777 | − "generated": null, | |
| 778 | − "identity": null, | |
| 779 | − "name": "resource_type", | |
| 780 | − "entityType": "columns", | |
| 781 | − "schema": "public", | |
| 782 | − "table": "policies" | |
| 783 | − }, | |
| 784 | − { | |
| 785 | − "type": "text", | |
| 786 | − "typeSchema": null, | |
| 787 | − "notNull": true, | |
| 788 | − "dimensions": 0, | |
| 789 | − "default": null, | |
| 790 | − "generated": null, | |
| 791 | − "identity": null, | |
| 792 | − "name": "resource_pattern", | |
| 793 | − "entityType": "columns", | |
| 794 | − "schema": "public", | |
| 795 | − "table": "policies" | |
| 796 | − }, | |
| 797 | − { | |
| 798 | − "type": "text", | |
| 799 | − "typeSchema": null, | |
| 800 | − "notNull": true, | |
| 801 | − "dimensions": 0, | |
| 802 | − "default": null, | |
| 803 | − "generated": null, | |
| 804 | − "identity": null, | |
| 805 | − "name": "match_type", | |
| 806 | − "entityType": "columns", | |
| 807 | − "schema": "public", | |
| 808 | − "table": "policies" | |
| 809 | − }, | |
| 810 | − { | |
| 811 | − "type": "text", | |
| 812 | − "typeSchema": null, | |
| 813 | − "notNull": true, | |
| 814 | − "dimensions": 0, | |
| 815 | − "default": null, | |
| 816 | − "generated": null, | |
| 817 | − "identity": null, | |
| 818 | − "name": "effect", | |
| 819 | − "entityType": "columns", | |
| 820 | − "schema": "public", | |
| 821 | − "table": "policies" | |
| 822 | − }, | |
| 823 | − { | |
| 824 | − "type": "text", | |
| 825 | − "typeSchema": null, | |
| 826 | − "notNull": true, | |
| 827 | − "dimensions": 0, | |
| 828 | − "default": null, | |
| 829 | − "generated": null, | |
| 830 | − "identity": null, | |
| 831 | − "name": "approval_mode", | |
| 832 | − "entityType": "columns", | |
| 833 | − "schema": "public", | |
| 834 | − "table": "policies" | |
| 835 | − }, | |
| 836 | − { | |
| 837 | − "type": "text", | |
| 838 | − "typeSchema": null, | |
| 839 | − "notNull": false, | |
| 840 | − "dimensions": 0, | |
| 841 | − "default": null, | |
| 842 | − "generated": null, | |
| 843 | − "identity": null, | |
| 844 | − "name": "argument_conditions_json", | |
| 845 | − "entityType": "columns", | |
| 846 | − "schema": "public", | |
| 847 | − "table": "policies" | |
| 848 | − }, | |
| 849 | − { | |
| 850 | − "type": "bigint", | |
| 851 | − "typeSchema": null, | |
| 852 | − "notNull": true, | |
| 853 | − "dimensions": 0, | |
| 854 | − "default": null, | |
| 855 | − "generated": null, | |
| 856 | − "identity": null, | |
| 857 | − "name": "priority", | |
| 858 | − "entityType": "columns", | |
| 859 | − "schema": "public", | |
| 860 | − "table": "policies" | |
| 861 | − }, | |
| 862 | − { | |
| 863 | − "type": "boolean", | |
| 864 | − "typeSchema": null, | |
| 865 | − "notNull": true, | |
| 866 | − "dimensions": 0, | |
| 867 | − "default": null, | |
| 868 | − "generated": null, | |
| 869 | − "identity": null, | |
| 870 | − "name": "enabled", | |
| 871 | − "entityType": "columns", | |
| 872 | − "schema": "public", | |
| 873 | − "table": "policies" | |
| 874 | − }, | |
| 875 | − { | |
| 876 | − "type": "bigint", | |
| 877 | − "typeSchema": null, | |
| 878 | − "notNull": true, | |
| 879 | − "dimensions": 0, | |
| 880 | − "default": null, | |
| 881 | − "generated": null, | |
| 882 | − "identity": null, | |
| 883 | − "name": "created_at", | |
| 884 | − "entityType": "columns", | |
| 885 | − "schema": "public", | |
| 886 | − "table": "policies" | |
| 887 | − }, | |
| 888 | − { | |
| 889 | − "type": "bigint", | |
| 890 | − "typeSchema": null, | |
| 891 | − "notNull": true, | |
| 892 | − "dimensions": 0, | |
| 893 | − "default": null, | |
| 894 | − "generated": null, | |
| 895 | − "identity": null, | |
| 896 | − "name": "updated_at", | |
| 897 | − "entityType": "columns", | |
| 898 | − "schema": "public", | |
| 899 | − "table": "policies" | |
| 900 | − }, | |
| 901 | − { | |
| 902 | − "type": "text", | |
| 903 | − "typeSchema": null, | |
| 904 | − "notNull": true, | |
| 905 | − "dimensions": 0, | |
| 906 | − "default": null, | |
| 907 | − "generated": null, | |
| 908 | − "identity": null, | |
| 909 | − "name": "workspace_id", | |
| 910 | − "entityType": "columns", | |
| 911 | − "schema": "public", | |
| 912 | − "table": "source_credential_bindings" | |
| 913 | − }, | |
| 914 | − { | |
| 915 | − "type": "text", | |
| 916 | − "typeSchema": null, | |
| 917 | − "notNull": true, | |
| 918 | − "dimensions": 0, | |
| 919 | − "default": null, | |
| 920 | − "generated": null, | |
| 921 | − "identity": null, | |
| 922 | − "name": "source_id", | |
| 923 | − "entityType": "columns", | |
| 924 | − "schema": "public", | |
| 925 | − "table": "source_credential_bindings" | |
| 926 | − }, | |
| 927 | − { | |
| 928 | − "type": "text", | |
| 929 | − "typeSchema": null, | |
| 930 | − "notNull": false, | |
| 931 | − "dimensions": 0, | |
| 932 | − "default": null, | |
| 933 | − "generated": null, | |
| 934 | − "identity": null, | |
| 935 | − "name": "token_provider_id", | |
| 936 | − "entityType": "columns", | |
| 937 | − "schema": "public", | |
| 938 | − "table": "source_credential_bindings" | |
| 939 | − }, | |
| 940 | − { | |
| 941 | − "type": "text", | |
| 942 | − "typeSchema": null, | |
| 943 | − "notNull": false, | |
| 944 | − "dimensions": 0, | |
| 945 | − "default": null, | |
| 946 | − "generated": null, | |
| 947 | − "identity": null, | |
| 948 | − "name": "token_handle", | |
| 949 | − "entityType": "columns", | |
| 950 | − "schema": "public", | |
| 951 | − "table": "source_credential_bindings" | |
| 952 | − }, | |
| 953 | − { | |
| 954 | − "type": "text", | |
| 955 | − "typeSchema": null, | |
| 956 | − "notNull": false, | |
| 957 | − "dimensions": 0, | |
| 958 | − "default": null, | |
| 959 | − "generated": null, | |
| 960 | − "identity": null, | |
| 961 | − "name": "refresh_token_provider_id", | |
| 962 | − "entityType": "columns", | |
| 963 | − "schema": "public", | |
| 964 | − "table": "source_credential_bindings" | |
| 965 | − }, | |
| 966 | − { | |
| 967 | − "type": "text", | |
| 968 | − "typeSchema": null, | |
| 969 | − "notNull": false, | |
| 970 | − "dimensions": 0, | |
| 971 | − "default": null, | |
| 972 | − "generated": null, | |
| 973 | − "identity": null, | |
| 974 | − "name": "refresh_token_handle", | |
| 975 | − "entityType": "columns", | |
| 976 | − "schema": "public", | |
| 977 | − "table": "source_credential_bindings" | |
| 978 | − }, | |
| 979 | − { | |
| 980 | − "type": "bigint", | |
| 981 | − "typeSchema": null, | |
| 982 | − "notNull": true, | |
| 983 | − "dimensions": 0, | |
| 984 | − "default": null, | |
| 985 | − "generated": null, | |
| 986 | − "identity": null, | |
| 987 | − "name": "created_at", | |
| 988 | − "entityType": "columns", | |
| 989 | − "schema": "public", | |
| 990 | − "table": "source_credential_bindings" | |
| 991 | − }, | |
| 992 | − { | |
| 993 | − "type": "bigint", | |
| 994 | − "typeSchema": null, | |
| 995 | − "notNull": true, | |
| 996 | − "dimensions": 0, | |
| 997 | − "default": null, | |
| 998 | − "generated": null, | |
| 999 | − "identity": null, | |
| 1000 | − "name": "updated_at", | |
| 1001 | − "entityType": "columns", | |
| 1002 | − "schema": "public", | |
| 1003 | − "table": "source_credential_bindings" | |
| 1004 | − }, | |
| 1005 | − { | |
| 1006 | − "type": "text", | |
| 1007 | − "typeSchema": null, | |
| 1008 | − "notNull": true, | |
| 1009 | − "dimensions": 0, | |
| 1010 | − "default": null, | |
| 1011 | − "generated": null, | |
| 1012 | − "identity": null, | |
| 1013 | − "name": "workspace_id", | |
| 1014 | − "entityType": "columns", | |
| 1015 | − "schema": "public", | |
| 1016 | − "table": "sources" | |
| 1017 | − }, | |
| 1018 | − { | |
| 1019 | − "type": "text", | |
| 1020 | − "typeSchema": null, | |
| 1021 | − "notNull": true, | |
| 1022 | − "dimensions": 0, | |
| 1023 | − "default": null, | |
| 1024 | − "generated": null, | |
| 1025 | − "identity": null, | |
| 1026 | − "name": "source_id", | |
| 1027 | − "entityType": "columns", | |
| 1028 | − "schema": "public", | |
| 1029 | − "table": "sources" | |
| 1030 | − }, | |
| 1031 | − { | |
| 1032 | − "type": "text", | |
| 1033 | − "typeSchema": null, | |
| 1034 | − "notNull": true, | |
| 1035 | − "dimensions": 0, | |
| 1036 | − "default": null, | |
| 1037 | − "generated": null, | |
| 1038 | − "identity": null, | |
| 1039 | − "name": "name", | |
| 1040 | − "entityType": "columns", | |
| 1041 | − "schema": "public", | |
| 1042 | − "table": "sources" | |
| 1043 | − }, | |
| 1044 | − { | |
| 1045 | − "type": "text", | |
| 1046 | − "typeSchema": null, | |
| 1047 | − "notNull": true, | |
| 1048 | − "dimensions": 0, | |
| 1049 | − "default": null, | |
| 1050 | − "generated": null, | |
| 1051 | − "identity": null, | |
| 1052 | − "name": "kind", | |
| 1053 | − "entityType": "columns", | |
| 1054 | − "schema": "public", | |
| 1055 | − "table": "sources" | |
| 1056 | − }, | |
| 1057 | − { | |
| 1058 | − "type": "text", | |
| 1059 | − "typeSchema": null, | |
| 1060 | − "notNull": true, | |
| 1061 | − "dimensions": 0, | |
| 1062 | − "default": null, | |
| 1063 | − "generated": null, | |
| 1064 | − "identity": null, | |
| 1065 | − "name": "endpoint", | |
| 1066 | − "entityType": "columns", | |
| 1067 | − "schema": "public", | |
| 1068 | − "table": "sources" | |
| 1069 | − }, | |
| 1070 | − { | |
| 1071 | − "type": "text", | |
| 1072 | − "typeSchema": null, | |
| 1073 | − "notNull": true, | |
| 1074 | − "dimensions": 0, | |
| 1075 | − "default": null, | |
| 1076 | − "generated": null, | |
| 1077 | − "identity": null, | |
| 1078 | − "name": "status", | |
| 1079 | − "entityType": "columns", | |
| 1080 | − "schema": "public", | |
| 1081 | − "table": "sources" | |
| 1082 | − }, | |
| 1083 | − { | |
| 1084 | − "type": "boolean", | |
| 1085 | − "typeSchema": null, | |
| 1086 | − "notNull": true, | |
| 1087 | − "dimensions": 0, | |
| 1088 | − "default": null, | |
| 1089 | − "generated": null, | |
| 1090 | − "identity": null, | |
| 1091 | − "name": "enabled", | |
| 1092 | − "entityType": "columns", | |
| 1093 | − "schema": "public", | |
| 1094 | − "table": "sources" | |
| 1095 | − }, | |
| 1096 | − { | |
| 1097 | − "type": "text", | |
| 1098 | − "typeSchema": null, | |
| 1099 | − "notNull": false, | |
| 1100 | − "dimensions": 0, | |
| 1101 | − "default": null, | |
| 1102 | − "generated": null, | |
| 1103 | − "identity": null, | |
| 1104 | − "name": "namespace", | |
| 1105 | − "entityType": "columns", | |
| 1106 | − "schema": "public", | |
| 1107 | − "table": "sources" | |
| 1108 | − }, | |
| 1109 | − { | |
| 1110 | − "type": "text", | |
| 1111 | − "typeSchema": null, | |
| 1112 | − "notNull": false, | |
| 1113 | − "dimensions": 0, | |
| 1114 | − "default": null, | |
| 1115 | − "generated": null, | |
| 1116 | − "identity": null, | |
| 1117 | − "name": "transport", | |
| 1118 | − "entityType": "columns", | |
| 1119 | − "schema": "public", | |
| 1120 | − "table": "sources" | |
| 1121 | − }, | |
| 1122 | − { | |
| 1123 | − "type": "text", | |
| 1124 | − "typeSchema": null, | |
| 1125 | − "notNull": false, | |
| 1126 | − "dimensions": 0, | |
| 1127 | − "default": null, | |
| 1128 | − "generated": null, | |
| 1129 | − "identity": null, | |
| 1130 | − "name": "query_params_json", | |
| 1131 | − "entityType": "columns", | |
| 1132 | − "schema": "public", | |
| 1133 | − "table": "sources" | |
| 1134 | − }, | |
| 1135 | − { | |
| 1136 | − "type": "text", | |
| 1137 | − "typeSchema": null, | |
| 1138 | − "notNull": false, | |
| 1139 | − "dimensions": 0, | |
| 1140 | − "default": null, | |
| 1141 | − "generated": null, | |
| 1142 | − "identity": null, | |
| 1143 | − "name": "headers_json", | |
| 1144 | − "entityType": "columns", | |
| 1145 | − "schema": "public", | |
| 1146 | − "table": "sources" | |
| 1147 | − }, | |
| 1148 | − { | |
| 1149 | − "type": "text", | |
| 1150 | − "typeSchema": null, | |
| 1151 | − "notNull": false, | |
| 1152 | − "dimensions": 0, | |
| 1153 | − "default": null, | |
| 1154 | − "generated": null, | |
| 1155 | − "identity": null, | |
| 1156 | − "name": "spec_url", | |
| 1157 | − "entityType": "columns", | |
| 1158 | − "schema": "public", | |
| 1159 | − "table": "sources" | |
| 1160 | − }, | |
| 1161 | − { | |
| 1162 | − "type": "text", | |
| 1163 | − "typeSchema": null, | |
| 1164 | − "notNull": false, | |
| 1165 | − "dimensions": 0, | |
| 1166 | − "default": null, | |
| 1167 | − "generated": null, | |
| 1168 | − "identity": null, | |
| 1169 | − "name": "default_headers_json", | |
| 1170 | − "entityType": "columns", | |
| 1171 | − "schema": "public", | |
| 1172 | − "table": "sources" | |
| 1173 | − }, | |
| 1174 | − { | |
| 1175 | − "type": "text", | |
| 1176 | − "typeSchema": null, | |
| 1177 | − "notNull": true, | |
| 1178 | − "dimensions": 0, | |
| 1179 | − "default": null, | |
| 1180 | − "generated": null, | |
| 1181 | − "identity": null, | |
| 1182 | − "name": "auth_kind", | |
| 1183 | − "entityType": "columns", | |
| 1184 | − "schema": "public", | |
| 1185 | − "table": "sources" | |
| 1186 | − }, | |
| 1187 | − { | |
| 1188 | − "type": "text", | |
| 1189 | − "typeSchema": null, | |
| 1190 | − "notNull": false, | |
| 1191 | − "dimensions": 0, | |
| 1192 | − "default": null, | |
| 1193 | − "generated": null, | |
| 1194 | − "identity": null, | |
| 1195 | − "name": "auth_header_name", | |
| 1196 | − "entityType": "columns", | |
| 1197 | − "schema": "public", | |
| 1198 | − "table": "sources" | |
| 1199 | − }, | |
| 1200 | − { | |
| 1201 | − "type": "text", | |
| 1202 | − "typeSchema": null, | |
| 1203 | − "notNull": false, | |
| 1204 | − "dimensions": 0, | |
| 1205 | − "default": null, | |
| 1206 | − "generated": null, | |
| 1207 | − "identity": null, | |
| 1208 | − "name": "auth_prefix", | |
| 1209 | − "entityType": "columns", | |
| 1210 | − "schema": "public", | |
| 1211 | − "table": "sources" | |
| 1212 | − }, | |
| 1213 | − { | |
| 1214 | − "type": "text", | |
| 1215 | − "typeSchema": null, | |
| 1216 | − "notNull": false, | |
| 1217 | − "dimensions": 0, | |
| 1218 | − "default": null, | |
| 1219 | − "generated": null, | |
| 1220 | − "identity": null, | |
| 1221 | − "name": "source_hash", | |
| 1222 | − "entityType": "columns", | |
| 1223 | − "schema": "public", | |
| 1224 | − "table": "sources" | |
| 1225 | − }, | |
| 1226 | − { | |
| 1227 | − "type": "text", | |
| 1228 | − "typeSchema": null, | |
| 1229 | − "notNull": false, | |
| 1230 | − "dimensions": 0, | |
| 1231 | − "default": null, | |
| 1232 | − "generated": null, | |
| 1233 | − "identity": null, | |
| 1234 | − "name": "last_error", | |
| 1235 | − "entityType": "columns", | |
| 1236 | − "schema": "public", | |
| 1237 | − "table": "sources" | |
| 1238 | − }, | |
| 1239 | − { | |
| 1240 | − "type": "bigint", | |
| 1241 | − "typeSchema": null, | |
| 1242 | − "notNull": true, | |
| 1243 | − "dimensions": 0, | |
| 1244 | − "default": null, | |
| 1245 | − "generated": null, | |
| 1246 | − "identity": null, | |
| 1247 | − "name": "created_at", | |
| 1248 | − "entityType": "columns", | |
| 1249 | − "schema": "public", | |
| 1250 | − "table": "sources" | |
| 1251 | − }, | |
| 1252 | − { | |
| 1253 | − "type": "bigint", | |
| 1254 | − "typeSchema": null, | |
| 1255 | − "notNull": true, | |
| 1256 | − "dimensions": 0, | |
| 1257 | − "default": null, | |
| 1258 | − "generated": null, | |
| 1259 | − "identity": null, | |
| 1260 | − "name": "updated_at", | |
| 1261 | − "entityType": "columns", | |
| 1262 | − "schema": "public", | |
| 1263 | − "table": "sources" | |
| 1264 | − }, | |
| 1265 | − { | |
| 1266 | − "type": "text", | |
| 1267 | − "typeSchema": null, | |
| 1268 | − "notNull": true, | |
| 1269 | − "dimensions": 0, | |
| 1270 | − "default": null, | |
| 1271 | − "generated": null, | |
| 1272 | − "identity": null, | |
| 1273 | − "name": "id", | |
| 1274 | − "entityType": "columns", | |
| 1275 | − "schema": "public", | |
| 1276 | − "table": "workspaces" | |
| 1277 | − }, | |
| 1278 | − { | |
| 1279 | − "type": "text", | |
| 1280 | − "typeSchema": null, | |
| 1281 | − "notNull": true, | |
| 1282 | − "dimensions": 0, | |
| 1283 | − "default": null, | |
| 1284 | − "generated": null, | |
| 1285 | − "identity": null, | |
| 1286 | − "name": "organization_id", | |
| 1287 | − "entityType": "columns", | |
| 1288 | − "schema": "public", | |
| 1289 | − "table": "workspaces" | |
| 1290 | − }, | |
| 1291 | − { | |
| 1292 | − "type": "text", | |
| 1293 | − "typeSchema": null, | |
| 1294 | − "notNull": true, | |
| 1295 | − "dimensions": 0, | |
| 1296 | − "default": null, | |
| 1297 | − "generated": null, | |
| 1298 | − "identity": null, | |
| 1299 | − "name": "name", | |
| 1300 | − "entityType": "columns", | |
| 1301 | − "schema": "public", | |
| 1302 | − "table": "workspaces" | |
| 1303 | − }, | |
| 1304 | − { | |
| 1305 | − "type": "text", | |
| 1306 | − "typeSchema": null, | |
| 1307 | − "notNull": false, | |
| 1308 | − "dimensions": 0, | |
| 1309 | − "default": null, | |
| 1310 | − "generated": null, | |
| 1311 | − "identity": null, | |
| 1312 | − "name": "created_by_account_id", | |
| 1313 | − "entityType": "columns", | |
| 1314 | − "schema": "public", | |
| 1315 | − "table": "workspaces" | |
| 1316 | − }, | |
| 1317 | − { | |
| 1318 | − "type": "bigint", | |
| 1319 | − "typeSchema": null, | |
| 1320 | − "notNull": true, | |
| 1321 | − "dimensions": 0, | |
| 1322 | − "default": null, | |
| 1323 | − "generated": null, | |
| 1324 | − "identity": null, | |
| 1325 | − "name": "created_at", | |
| 1326 | − "entityType": "columns", | |
| 1327 | − "schema": "public", | |
| 1328 | − "table": "workspaces" | |
| 1329 | − }, | |
| 1330 | − { | |
| 1331 | − "type": "bigint", | |
| 1332 | − "typeSchema": null, | |
| 1333 | − "notNull": true, | |
| 1334 | − "dimensions": 0, | |
| 1335 | − "default": null, | |
| 1336 | − "generated": null, | |
| 1337 | − "identity": null, | |
| 1338 | − "name": "updated_at", | |
| 1339 | − "entityType": "columns", | |
| 1340 | − "schema": "public", | |
| 1341 | − "table": "workspaces" | |
| 1342 | − }, | |
| 1343 | − { | |
| 1344 | − "nameExplicit": true, | |
| 1345 | − "columns": [ | |
| 1346 | − { | |
| 1347 | − "value": "provider", | |
| 1348 | − "isExpression": false, | |
| 1349 | − "asc": true, | |
| 1350 | − "nullsFirst": false, | |
| 1351 | − "opclass": null | |
| 1352 | − }, | |
| 1353 | − { | |
| 1354 | − "value": "subject", | |
| 1355 | − "isExpression": false, | |
| 1356 | − "asc": true, | |
| 1357 | − "nullsFirst": false, | |
| 1358 | − "opclass": null | |
| 1359 | − } | |
| 1360 | − ], | |
| 1361 | − "isUnique": true, | |
| 1362 | − "where": null, | |
| 1363 | − "with": "", | |
| 1364 | − "method": "btree", | |
| 1365 | − "concurrently": false, | |
| 1366 | − "name": "accounts_provider_subject_idx", | |
| 1367 | − "entityType": "indexes", | |
| 1368 | − "schema": "public", | |
| 1369 | − "table": "accounts" | |
| 1370 | − }, | |
| 1371 | − { | |
| 1372 | − "nameExplicit": true, | |
| 1373 | − "columns": [ | |
| 1374 | − { | |
| 1375 | − "value": "updated_at", | |
| 1376 | − "isExpression": false, | |
| 1377 | − "asc": true, | |
| 1378 | − "nullsFirst": false, | |
| 1379 | − "opclass": null | |
| 1380 | − }, | |
| 1381 | − { | |
| 1382 | − "value": "id", | |
| 1383 | − "isExpression": false, | |
| 1384 | − "asc": true, | |
| 1385 | − "nullsFirst": false, | |
| 1386 | − "opclass": null | |
| 1387 | − } | |
| 1388 | − ], | |
| 1389 | − "isUnique": false, | |
| 1390 | − "where": null, | |
| 1391 | − "with": "", | |
| 1392 | − "method": "btree", | |
| 1393 | − "concurrently": false, | |
| 1394 | − "name": "accounts_updated_idx", | |
| 1395 | − "entityType": "indexes", | |
| 1396 | − "schema": "public", | |
| 1397 | − "table": "accounts" | |
| 1398 | − }, | |
| 1399 | − { | |
| 1400 | − "nameExplicit": true, | |
| 1401 | − "columns": [ | |
| 1402 | − { | |
| 1403 | − "value": "execution_id", | |
| 1404 | − "isExpression": false, | |
| 1405 | − "asc": true, | |
| 1406 | − "nullsFirst": false, | |
| 1407 | − "opclass": null | |
| 1408 | − }, | |
| 1409 | − { | |
| 1410 | − "value": "updated_at", | |
| 1411 | − "isExpression": false, | |
| 1412 | − "asc": true, | |
| 1413 | − "nullsFirst": false, | |
| 1414 | − "opclass": null | |
| 1415 | − }, | |
| 1416 | − { | |
| 1417 | − "value": "id", | |
| 1418 | − "isExpression": false, | |
| 1419 | − "asc": true, | |
| 1420 | − "nullsFirst": false, | |
| 1421 | − "opclass": null | |
| 1422 | − } | |
| 1423 | − ], | |
| 1424 | − "isUnique": false, | |
| 1425 | − "where": null, | |
| 1426 | − "with": "", | |
| 1427 | − "method": "btree", | |
| 1428 | − "concurrently": false, | |
| 1429 | − "name": "execution_interactions_execution_idx", | |
| 1430 | − "entityType": "indexes", | |
| 1431 | − "schema": "public", | |
| 1432 | − "table": "execution_interactions" | |
| 1433 | − }, | |
| 1434 | − { | |
| 1435 | − "nameExplicit": true, | |
| 1436 | − "columns": [ | |
| 1437 | − { | |
| 1438 | − "value": "workspace_id", | |
| 1439 | − "isExpression": false, | |
| 1440 | − "asc": true, | |
| 1441 | − "nullsFirst": false, | |
| 1442 | − "opclass": null | |
| 1443 | − }, | |
| 1444 | − { | |
| 1445 | − "value": "updated_at", | |
| 1446 | − "isExpression": false, | |
| 1447 | − "asc": true, | |
| 1448 | − "nullsFirst": false, | |
| 1449 | − "opclass": null | |
| 1450 | − }, | |
| 1451 | − { | |
| 1452 | − "value": "id", | |
| 1453 | − "isExpression": false, | |
| 1454 | − "asc": true, | |
| 1455 | − "nullsFirst": false, | |
| 1456 | − "opclass": null | |
| 1457 | − } | |
| 1458 | − ], | |
| 1459 | − "isUnique": false, | |
| 1460 | − "where": null, | |
| 1461 | − "with": "", | |
| 1462 | − "method": "btree", | |
| 1463 | − "concurrently": false, | |
| 1464 | − "name": "executions_workspace_idx", | |
| 1465 | − "entityType": "indexes", | |
| 1466 | − "schema": "public", | |
| 1467 | − "table": "executions" | |
| 1468 | − }, | |
| 1469 | − { | |
| 1470 | − "nameExplicit": true, | |
| 1471 | − "columns": [ | |
| 1472 | − { | |
| 1473 | − "value": "organization_id", | |
| 1474 | − "isExpression": false, | |
| 1475 | − "asc": true, | |
| 1476 | − "nullsFirst": false, | |
| 1477 | − "opclass": null | |
| 1478 | − } | |
| 1479 | − ], | |
| 1480 | − "isUnique": false, | |
| 1481 | − "where": null, | |
| 1482 | − "with": "", | |
| 1483 | − "method": "btree", | |
| 1484 | − "concurrently": false, | |
| 1485 | − "name": "organization_memberships_org_idx", | |
| 1486 | − "entityType": "indexes", | |
| 1487 | − "schema": "public", | |
| 1488 | − "table": "organization_memberships" | |
| 1489 | − }, | |
| 1490 | − { | |
| 1491 | − "nameExplicit": true, | |
| 1492 | − "columns": [ | |
| 1493 | − { | |
| 1494 | − "value": "account_id", | |
| 1495 | − "isExpression": false, | |
| 1496 | − "asc": true, | |
| 1497 | − "nullsFirst": false, | |
| 1498 | − "opclass": null | |
| 1499 | − } | |
| 1500 | − ], | |
| 1501 | − "isUnique": false, | |
| 1502 | − "where": null, | |
| 1503 | − "with": "", | |
| 1504 | − "method": "btree", | |
| 1505 | − "concurrently": false, | |
| 1506 | − "name": "organization_memberships_account_idx", | |
| 1507 | − "entityType": "indexes", | |
| 1508 | − "schema": "public", | |
| 1509 | − "table": "organization_memberships" | |
| 1510 | − }, | |
| 1511 | − { | |
| 1512 | − "nameExplicit": true, | |
| 1513 | − "columns": [ | |
| 1514 | − { | |
| 1515 | − "value": "organization_id", | |
| 1516 | − "isExpression": false, | |
| 1517 | − "asc": true, | |
| 1518 | − "nullsFirst": false, | |
| 1519 | − "opclass": null | |
| 1520 | − }, | |
| 1521 | − { | |
| 1522 | − "value": "account_id", | |
| 1523 | − "isExpression": false, | |
| 1524 | − "asc": true, | |
| 1525 | − "nullsFirst": false, | |
| 1526 | − "opclass": null | |
| 1527 | − } | |
| 1528 | − ], | |
| 1529 | − "isUnique": true, | |
| 1530 | − "where": null, | |
| 1531 | − "with": "", | |
| 1532 | − "method": "btree", | |
| 1533 | − "concurrently": false, | |
| 1534 | − "name": "organization_memberships_org_account_idx", | |
| 1535 | − "entityType": "indexes", | |
| 1536 | − "schema": "public", | |
| 1537 | − "table": "organization_memberships" | |
| 1538 | − }, | |
| 1539 | − { | |
| 1540 | − "nameExplicit": true, | |
| 1541 | − "columns": [ | |
| 1542 | − { | |
| 1543 | − "value": "slug", | |
| 1544 | − "isExpression": false, | |
| 1545 | − "asc": true, | |
| 1546 | − "nullsFirst": false, | |
| 1547 | − "opclass": null | |
| 1548 | − } | |
| 1549 | − ], | |
| 1550 | − "isUnique": true, | |
| 1551 | − "where": null, | |
| 1552 | − "with": "", | |
| 1553 | − "method": "btree", | |
| 1554 | − "concurrently": false, | |
| 1555 | − "name": "organizations_slug_idx", | |
| 1556 | − "entityType": "indexes", | |
| 1557 | − "schema": "public", | |
| 1558 | − "table": "organizations" | |
| 1559 | − }, | |
| 1560 | − { | |
| 1561 | − "nameExplicit": true, | |
| 1562 | − "columns": [ | |
| 1563 | − { | |
| 1564 | − "value": "updated_at", | |
| 1565 | − "isExpression": false, | |
| 1566 | − "asc": true, | |
| 1567 | − "nullsFirst": false, | |
| 1568 | − "opclass": null | |
| 1569 | − }, | |
| 1570 | − { | |
| 1571 | − "value": "id", | |
| 1572 | − "isExpression": false, | |
| 1573 | − "asc": true, | |
| 1574 | − "nullsFirst": false, | |
| 1575 | − "opclass": null | |
| 1576 | − } | |
| 1577 | − ], | |
| 1578 | − "isUnique": false, | |
| 1579 | − "where": null, | |
| 1580 | − "with": "", | |
| 1581 | − "method": "btree", | |
| 1582 | − "concurrently": false, | |
| 1583 | − "name": "organizations_updated_idx", | |
| 1584 | − "entityType": "indexes", | |
| 1585 | − "schema": "public", | |
| 1586 | − "table": "organizations" | |
| 1587 | − }, | |
| 1588 | − { | |
| 1589 | − "nameExplicit": true, | |
| 1590 | − "columns": [ | |
| 1591 | − { | |
| 1592 | − "value": "workspace_id", | |
| 1593 | − "isExpression": false, | |
| 1594 | − "asc": true, | |
| 1595 | − "nullsFirst": false, | |
| 1596 | − "opclass": null | |
| 1597 | − }, | |
| 1598 | − { | |
| 1599 | − "value": "updated_at", | |
| 1600 | − "isExpression": false, | |
| 1601 | − "asc": true, | |
| 1602 | − "nullsFirst": false, | |
| 1603 | − "opclass": null | |
| 1604 | − }, | |
| 1605 | − { | |
| 1606 | − "value": "id", | |
| 1607 | − "isExpression": false, | |
| 1608 | − "asc": true, | |
| 1609 | − "nullsFirst": false, | |
| 1610 | − "opclass": null | |
| 1611 | − } | |
| 1612 | − ], | |
| 1613 | − "isUnique": false, | |
| 1614 | − "where": null, | |
| 1615 | − "with": "", | |
| 1616 | − "method": "btree", | |
| 1617 | − "concurrently": false, | |
| 1618 | − "name": "policies_workspace_idx", | |
| 1619 | − "entityType": "indexes", | |
| 1620 | − "schema": "public", | |
| 1621 | − "table": "policies" | |
| 1622 | − }, | |
| 1623 | − { | |
| 1624 | − "nameExplicit": true, | |
| 1625 | − "columns": [ | |
| 1626 | − { | |
| 1627 | − "value": "workspace_id", | |
| 1628 | − "isExpression": false, | |
| 1629 | − "asc": true, | |
| 1630 | − "nullsFirst": false, | |
| 1631 | − "opclass": null | |
| 1632 | − }, | |
| 1633 | − { | |
| 1634 | − "value": "updated_at", | |
| 1635 | − "isExpression": false, | |
| 1636 | − "asc": true, | |
| 1637 | − "nullsFirst": false, | |
| 1638 | − "opclass": null | |
| 1639 | − }, | |
| 1640 | − { | |
| 1641 | − "value": "source_id", | |
| 1642 | − "isExpression": false, | |
| 1643 | − "asc": true, | |
| 1644 | − "nullsFirst": false, | |
| 1645 | − "opclass": null | |
| 1646 | − } | |
| 1647 | − ], | |
| 1648 | − "isUnique": false, | |
| 1649 | − "where": null, | |
| 1650 | − "with": "", | |
| 1651 | − "method": "btree", | |
| 1652 | − "concurrently": false, | |
| 1653 | − "name": "source_credential_bindings_workspace_idx", | |
| 1654 | − "entityType": "indexes", | |
| 1655 | − "schema": "public", | |
| 1656 | − "table": "source_credential_bindings" | |
| 1657 | − }, | |
| 1658 | − { | |
| 1659 | − "nameExplicit": true, | |
| 1660 | − "columns": [ | |
| 1661 | − { | |
| 1662 | − "value": "workspace_id", | |
| 1663 | − "isExpression": false, | |
| 1664 | − "asc": true, | |
| 1665 | − "nullsFirst": false, | |
| 1666 | − "opclass": null | |
| 1667 | − }, | |
| 1668 | − { | |
| 1669 | − "value": "name", | |
| 1670 | − "isExpression": false, | |
| 1671 | − "asc": true, | |
| 1672 | − "nullsFirst": false, | |
| 1673 | − "opclass": null | |
| 1674 | − } | |
| 1675 | − ], | |
| 1676 | − "isUnique": true, | |
| 1677 | − "where": null, | |
| 1678 | − "with": "", | |
| 1679 | − "method": "btree", | |
| 1680 | − "concurrently": false, | |
| 1681 | − "name": "sources_workspace_name_idx", | |
| 1682 | − "entityType": "indexes", | |
| 1683 | − "schema": "public", | |
| 1684 | − "table": "sources" | |
| 1685 | − }, | |
| 1686 | − { | |
| 1687 | − "nameExplicit": true, | |
| 1688 | − "columns": [ | |
| 1689 | − { | |
| 1690 | − "value": "organization_id", | |
| 1691 | − "isExpression": false, | |
| 1692 | − "asc": true, | |
| 1693 | − "nullsFirst": false, | |
| 1694 | − "opclass": null | |
| 1695 | − } | |
| 1696 | − ], | |
| 1697 | − "isUnique": false, | |
| 1698 | − "where": null, | |
| 1699 | − "with": "", | |
| 1700 | − "method": "btree", | |
| 1701 | − "concurrently": false, | |
| 1702 | − "name": "workspaces_org_idx", | |
| 1703 | − "entityType": "indexes", | |
| 1704 | − "schema": "public", | |
| 1705 | − "table": "workspaces" | |
| 1706 | − }, | |
| 1707 | − { | |
| 1708 | − "nameExplicit": true, | |
| 1709 | − "columns": [ | |
| 1710 | − { | |
| 1711 | − "value": "organization_id", | |
| 1712 | − "isExpression": false, | |
| 1713 | − "asc": true, | |
| 1714 | − "nullsFirst": false, | |
| 1715 | − "opclass": null | |
| 1716 | − }, | |
| 1717 | − { | |
| 1718 | − "value": "name", | |
| 1719 | − "isExpression": false, | |
| 1720 | − "asc": true, | |
| 1721 | − "nullsFirst": false, | |
| 1722 | − "opclass": null | |
| 1723 | − } | |
| 1724 | − ], | |
| 1725 | − "isUnique": true, | |
| 1726 | − "where": null, | |
| 1727 | − "with": "", | |
| 1728 | − "method": "btree", | |
| 1729 | − "concurrently": false, | |
| 1730 | − "name": "workspaces_org_name_idx", | |
| 1731 | − "entityType": "indexes", | |
| 1732 | − "schema": "public", | |
| 1733 | − "table": "workspaces" | |
| 1734 | − }, | |
| 1735 | − { | |
| 1736 | − "columns": [ | |
| 1737 | − "workspace_id", | |
| 1738 | − "source_id" | |
| 1739 | − ], | |
| 1740 | − "nameExplicit": false, | |
| 1741 | − "name": "source_credential_bindings_pkey", | |
| 1742 | − "entityType": "pks", | |
| 1743 | − "schema": "public", | |
| 1744 | − "table": "source_credential_bindings" | |
| 1745 | − }, | |
| 1746 | − { | |
| 1747 | − "columns": [ | |
| 1748 | − "workspace_id", | |
| 1749 | − "source_id" | |
| 1750 | − ], | |
| 1751 | − "nameExplicit": false, | |
| 1752 | − "name": "sources_pkey", | |
| 1753 | − "entityType": "pks", | |
| 1754 | − "schema": "public", | |
| 1755 | − "table": "sources" | |
| 1756 | − }, | |
| 1757 | − { | |
| 1758 | − "columns": [ | |
| 1759 | − "id" | |
| 1760 | − ], | |
| 1761 | − "nameExplicit": false, | |
| 1762 | − "name": "accounts_pkey", | |
| 1763 | − "schema": "public", | |
| 1764 | − "table": "accounts", | |
| 1765 | − "entityType": "pks" | |
| 1766 | − }, | |
| 1767 | − { | |
| 1768 | − "columns": [ | |
| 1769 | − "id" | |
| 1770 | − ], | |
| 1771 | − "nameExplicit": false, | |
| 1772 | − "name": "execution_interactions_pkey", | |
| 1773 | − "schema": "public", | |
| 1774 | − "table": "execution_interactions", | |
| 1775 | − "entityType": "pks" | |
| 1776 | − }, | |
| 1777 | − { | |
| 1778 | − "columns": [ | |
| 1779 | − "id" | |
| 1780 | − ], | |
| 1781 | − "nameExplicit": false, | |
| 1782 | − "name": "executions_pkey", | |
| 1783 | − "schema": "public", | |
| 1784 | − "table": "executions", | |
| 1785 | − "entityType": "pks" | |
| 1786 | − }, | |
| 1787 | − { | |
| 1788 | − "columns": [ | |
| 1789 | − "id" | |
| 1790 | − ], | |
| 1791 | − "nameExplicit": false, | |
| 1792 | − "name": "local_installations_pkey", | |
| 1793 | − "schema": "public", | |
| 1794 | − "table": "local_installations", | |
| 1795 | − "entityType": "pks" | |
| 1796 | − }, | |
| 1797 | − { | |
| 1798 | − "columns": [ | |
| 1799 | − "id" | |
| 1800 | − ], | |
| 1801 | − "nameExplicit": false, | |
| 1802 | − "name": "organization_memberships_pkey", | |
| 1803 | − "schema": "public", | |
| 1804 | − "table": "organization_memberships", | |
| 1805 | − "entityType": "pks" | |
| 1806 | − }, | |
| 1807 | − { | |
| 1808 | − "columns": [ | |
| 1809 | − "id" | |
| 1810 | − ], | |
| 1811 | − "nameExplicit": false, | |
| 1812 | − "name": "organizations_pkey", | |
| 1813 | − "schema": "public", | |
| 1814 | − "table": "organizations", | |
| 1815 | − "entityType": "pks" | |
| 1816 | − }, | |
| 1817 | − { | |
| 1818 | − "columns": [ | |
| 1819 | − "id" | |
| 1820 | − ], | |
| 1821 | − "nameExplicit": false, | |
| 1822 | − "name": "policies_pkey", | |
| 1823 | − "schema": "public", | |
| 1824 | − "table": "policies", | |
| 1825 | − "entityType": "pks" | |
| 1826 | − }, | |
| 1827 | − { | |
| 1828 | − "columns": [ | |
| 1829 | − "id" | |
| 1830 | − ], | |
| 1831 | − "nameExplicit": false, | |
| 1832 | − "name": "workspaces_pkey", | |
| 1833 | − "schema": "public", | |
| 1834 | − "table": "workspaces", | |
| 1835 | − "entityType": "pks" | |
| 1836 | − }, | |
| 1837 | − { | |
| 1838 | − "value": "\"provider\" in ('local', 'workos', 'service')", | |
| 1839 | − "name": "accounts_provider_check", | |
| 1840 | − "entityType": "checks", | |
| 1841 | − "schema": "public", | |
| 1842 | − "table": "accounts" | |
| 1843 | − }, | |
| 1844 | − { | |
| 1845 | − "value": "\"status\" in ('pending', 'resolved', 'cancelled')", | |
| 1846 | − "name": "execution_interactions_status_check", | |
| 1847 | − "entityType": "checks", | |
| 1848 | − "schema": "public", | |
| 1849 | − "table": "execution_interactions" | |
| 1850 | − }, | |
| 1851 | − { | |
| 1852 | − "value": "\"status\" in ('pending', 'running', 'waiting_for_interaction', 'completed', 'failed', 'cancelled')", | |
| 1853 | − "name": "executions_status_check", | |
| 1854 | − "entityType": "checks", | |
| 1855 | − "schema": "public", | |
| 1856 | − "table": "executions" | |
| 1857 | − }, | |
| 1858 | − { | |
| 1859 | − "value": "\"role\" in ('viewer', 'editor', 'admin', 'owner')", | |
| 1860 | − "name": "organization_memberships_role_check", | |
| 1861 | − "entityType": "checks", | |
| 1862 | − "schema": "public", | |
| 1863 | − "table": "organization_memberships" | |
| 1864 | − }, | |
| 1865 | − { | |
| 1866 | − "value": "\"status\" in ('invited', 'active', 'suspended', 'removed')", | |
| 1867 | − "name": "organization_memberships_status_check", | |
| 1868 | − "entityType": "checks", | |
| 1869 | − "schema": "public", | |
| 1870 | − "table": "organization_memberships" | |
| 1871 | − }, | |
| 1872 | − { | |
| 1873 | − "value": "\"status\" in ('active', 'suspended', 'archived')", | |
| 1874 | − "name": "organizations_status_check", | |
| 1875 | − "entityType": "checks", | |
| 1876 | − "schema": "public", | |
| 1877 | − "table": "organizations" | |
| 1878 | − }, | |
| 1879 | − { | |
| 1880 | − "value": "\"resource_type\" in ('all_tools', 'source', 'namespace', 'tool_path')", | |
| 1881 | − "name": "policies_resource_type_check", | |
| 1882 | − "entityType": "checks", | |
| 1883 | − "schema": "public", | |
| 1884 | − "table": "policies" | |
| 1885 | − }, | |
| 1886 | − { | |
| 1887 | − "value": "\"match_type\" in ('glob', 'exact')", | |
| 1888 | − "name": "policies_match_type_check", | |
| 1889 | − "entityType": "checks", | |
| 1890 | − "schema": "public", | |
| 1891 | − "table": "policies" | |
| 1892 | − }, | |
| 1893 | − { | |
| 1894 | − "value": "\"effect\" in ('allow', 'deny')", | |
| 1895 | − "name": "policies_effect_check", | |
| 1896 | − "entityType": "checks", | |
| 1897 | − "schema": "public", | |
| 1898 | − "table": "policies" | |
| 1899 | − }, | |
| 1900 | − { | |
| 1901 | − "value": "\"approval_mode\" in ('auto', 'required')", | |
| 1902 | − "name": "policies_approval_mode_check", | |
| 1903 | − "entityType": "checks", | |
| 1904 | − "schema": "public", | |
| 1905 | − "table": "policies" | |
| 1906 | − }, | |
| 1907 | − { | |
| 1908 | − "value": "\"kind\" in ('mcp', 'openapi', 'graphql', 'internal')", | |
| 1909 | − "name": "sources_kind_check", | |
| 1910 | − "entityType": "checks", | |
| 1911 | − "schema": "public", | |
| 1912 | − "table": "sources" | |
| 1913 | − }, | |
| 1914 | − { | |
| 1915 | − "value": "\"status\" in ('draft', 'probing', 'auth_required', 'connected', 'error')", | |
| 1916 | − "name": "sources_status_check", | |
| 1917 | − "entityType": "checks", | |
| 1918 | − "schema": "public", | |
| 1919 | − "table": "sources" | |
| 1920 | − }, | |
| 1921 | − { | |
| 1922 | − "value": "\"transport\" is null or \"transport\" in ('auto', 'streamable-http', 'sse')", | |
| 1923 | − "name": "sources_transport_check", | |
| 1924 | − "entityType": "checks", | |
| 1925 | − "schema": "public", | |
| 1926 | − "table": "sources" | |
| 1927 | − }, | |
| 1928 | − { | |
| 1929 | − "value": "\"auth_kind\" in ('none', 'bearer', 'oauth2')", | |
| 1930 | − "name": "sources_auth_kind_check", | |
| 1931 | − "entityType": "checks", | |
| 1932 | − "schema": "public", | |
| 1933 | − "table": "sources" | |
| 1934 | − } | |
| 1935 | − ], | |
| 1936 | − "renames": [] | |
| 1937 | −} | |
| @@ -1,39 +0,0 @@ | ||
| 1 | −CREATE TABLE "secret_materials" ( | |
| 2 | − "id" text PRIMARY KEY, | |
| 3 | − "purpose" text NOT NULL, | |
| 4 | − "value" text NOT NULL, | |
| 5 | − "created_at" bigint NOT NULL, | |
| 6 | − "updated_at" bigint NOT NULL, | |
| 7 | − CONSTRAINT "secret_materials_purpose_check" CHECK ("purpose" in ('auth_material', 'oauth_access_token', 'oauth_refresh_token', 'oauth_client_info')) | |
| 8 | −); | |
| 9 | −--> statement-breakpoint | |
| 10 | −CREATE TABLE "source_auth_sessions" ( | |
| 11 | − "id" text PRIMARY KEY, | |
| 12 | − "workspace_id" text NOT NULL, | |
| 13 | − "source_id" text NOT NULL, | |
| 14 | − "execution_id" text, | |
| 15 | − "interaction_id" text, | |
| 16 | − "strategy" text NOT NULL, | |
| 17 | − "status" text NOT NULL, | |
| 18 | − "endpoint" text NOT NULL, | |
| 19 | − "state" text NOT NULL, | |
| 20 | − "redirect_uri" text NOT NULL, | |
| 21 | − "scope" text, | |
| 22 | − "resource_metadata_url" text, | |
| 23 | − "authorization_server_url" text, | |
| 24 | − "resource_metadata_json" text, | |
| 25 | − "authorization_server_metadata_json" text, | |
| 26 | − "client_information_json" text, | |
| 27 | − "code_verifier" text, | |
| 28 | − "authorization_url" text, | |
| 29 | − "error_text" text, | |
| 30 | − "completed_at" bigint, | |
| 31 | − "created_at" bigint NOT NULL, | |
| 32 | − "updated_at" bigint NOT NULL, | |
| 33 | − CONSTRAINT "source_auth_sessions_strategy_check" CHECK ("strategy" in ('oauth2_authorization_code')), | |
| 34 | − CONSTRAINT "source_auth_sessions_status_check" CHECK ("status" in ('pending', 'completed', 'failed', 'cancelled')) | |
| 35 | −); | |
| 36 | −--> statement-breakpoint | |
| 37 | −CREATE INDEX "secret_materials_updated_idx" ON "secret_materials" ("updated_at","id");--> statement-breakpoint | |
| 38 | −CREATE INDEX "source_auth_sessions_workspace_idx" ON "source_auth_sessions" ("workspace_id","updated_at","id");--> statement-breakpoint | |
| 39 | −CREATE UNIQUE INDEX "source_auth_sessions_state_idx" ON "source_auth_sessions" ("state"); | |
| @@ -1,2425 +0,0 @@ | ||
| 1 | −{ | |
| 2 | − "version": "8", | |
| 3 | − "dialect": "postgres", | |
| 4 | − "id": "1141497b-c500-44ff-87db-6a440062becb", | |
| 5 | − "prevIds": [ | |
| 6 | − "bac1651d-7a30-4187-a7e2-74bc2b099486" | |
| 7 | − ], | |
| 8 | − "ddl": [ | |
| 9 | − { | |
| 10 | − "isRlsEnabled": false, | |
| 11 | − "name": "accounts", | |
| 12 | − "entityType": "tables", | |
| 13 | − "schema": "public" | |
| 14 | − }, | |
| 15 | − { | |
| 16 | − "isRlsEnabled": false, | |
| 17 | − "name": "execution_interactions", | |
| 18 | − "entityType": "tables", | |
| 19 | − "schema": "public" | |
| 20 | − }, | |
| 21 | − { | |
| 22 | − "isRlsEnabled": false, | |
| 23 | − "name": "executions", | |
| 24 | − "entityType": "tables", | |
| 25 | − "schema": "public" | |
| 26 | − }, | |
| 27 | − { | |
| 28 | − "isRlsEnabled": false, | |
| 29 | − "name": "local_installations", | |
| 30 | − "entityType": "tables", | |
| 31 | − "schema": "public" | |
| 32 | − }, | |
| 33 | − { | |
| 34 | − "isRlsEnabled": false, | |
| 35 | − "name": "organization_memberships", | |
| 36 | − "entityType": "tables", | |
| 37 | − "schema": "public" | |
| 38 | − }, | |
| 39 | − { | |
| 40 | − "isRlsEnabled": false, | |
| 41 | − "name": "organizations", | |
| 42 | − "entityType": "tables", | |
| 43 | − "schema": "public" | |
| 44 | − }, | |
| 45 | − { | |
| 46 | − "isRlsEnabled": false, | |
| 47 | − "name": "policies", | |
| 48 | − "entityType": "tables", | |
| 49 | − "schema": "public" | |
| 50 | − }, | |
| 51 | − { | |
| 52 | − "isRlsEnabled": false, | |
| 53 | − "name": "secret_materials", | |
| 54 | − "entityType": "tables", | |
| 55 | − "schema": "public" | |
| 56 | − }, | |
| 57 | − { | |
| 58 | − "isRlsEnabled": false, | |
| 59 | − "name": "source_auth_sessions", | |
| 60 | − "entityType": "tables", | |
| 61 | − "schema": "public" | |
| 62 | − }, | |
| 63 | − { | |
| 64 | − "isRlsEnabled": false, | |
| 65 | − "name": "source_credential_bindings", | |
| 66 | − "entityType": "tables", | |
| 67 | − "schema": "public" | |
| 68 | − }, | |
| 69 | − { | |
| 70 | − "isRlsEnabled": false, | |
| 71 | − "name": "sources", | |
| 72 | − "entityType": "tables", | |
| 73 | − "schema": "public" | |
| 74 | − }, | |
| 75 | − { | |
| 76 | − "isRlsEnabled": false, | |
| 77 | − "name": "workspaces", | |
| 78 | − "entityType": "tables", | |
| 79 | − "schema": "public" | |
| 80 | − }, | |
| 81 | − { | |
| 82 | − "type": "text", | |
| 83 | − "typeSchema": null, | |
| 84 | − "notNull": true, | |
| 85 | − "dimensions": 0, | |
| 86 | − "default": null, | |
| 87 | − "generated": null, | |
| 88 | − "identity": null, | |
| 89 | − "name": "id", | |
| 90 | − "entityType": "columns", | |
| 91 | − "schema": "public", | |
| 92 | − "table": "accounts" | |
| 93 | − }, | |
| 94 | − { | |
| 95 | − "type": "text", | |
| 96 | − "typeSchema": null, | |
| 97 | − "notNull": true, | |
| 98 | − "dimensions": 0, | |
| 99 | − "default": null, | |
| 100 | − "generated": null, | |
| 101 | − "identity": null, | |
| 102 | − "name": "provider", | |
| 103 | − "entityType": "columns", | |
| 104 | − "schema": "public", | |
| 105 | − "table": "accounts" | |
| 106 | − }, | |
| 107 | − { | |
| 108 | − "type": "text", | |
| 109 | − "typeSchema": null, | |
| 110 | − "notNull": true, | |
| 111 | − "dimensions": 0, | |
| 112 | − "default": null, | |
| 113 | − "generated": null, | |
| 114 | − "identity": null, | |
| 115 | − "name": "subject", | |
| 116 | − "entityType": "columns", | |
| 117 | − "schema": "public", | |
| 118 | − "table": "accounts" | |
| 119 | − }, | |
| 120 | − { | |
| 121 | − "type": "text", | |
| 122 | − "typeSchema": null, | |
| 123 | − "notNull": false, | |
| 124 | − "dimensions": 0, | |
| 125 | − "default": null, | |
| 126 | − "generated": null, | |
| 127 | − "identity": null, | |
| 128 | − "name": "email", | |
| 129 | − "entityType": "columns", | |
| 130 | − "schema": "public", | |
| 131 | − "table": "accounts" | |
| 132 | − }, | |
| 133 | − { | |
| 134 | − "type": "text", | |
| 135 | − "typeSchema": null, | |
| 136 | − "notNull": false, | |
| 137 | − "dimensions": 0, | |
| 138 | − "default": null, | |
| 139 | − "generated": null, | |
| 140 | − "identity": null, | |
| 141 | − "name": "display_name", | |
| 142 | − "entityType": "columns", | |
| 143 | − "schema": "public", | |
| 144 | − "table": "accounts" | |
| 145 | − }, | |
| 146 | − { | |
| 147 | − "type": "bigint", | |
| 148 | − "typeSchema": null, | |
| 149 | − "notNull": true, | |
| 150 | − "dimensions": 0, | |
| 151 | − "default": null, | |
| 152 | − "generated": null, | |
| 153 | − "identity": null, | |
| 154 | − "name": "created_at", | |
| 155 | − "entityType": "columns", | |
| 156 | − "schema": "public", | |
| 157 | − "table": "accounts" | |
| 158 | − }, | |
| 159 | − { | |
| 160 | − "type": "bigint", | |
| 161 | − "typeSchema": null, | |
| 162 | − "notNull": true, | |
| 163 | − "dimensions": 0, | |
| 164 | − "default": null, | |
| 165 | − "generated": null, | |
| 166 | − "identity": null, | |
| 167 | − "name": "updated_at", | |
| 168 | − "entityType": "columns", | |
| 169 | − "schema": "public", | |
| 170 | − "table": "accounts" | |
| 171 | − }, | |
| 172 | − { | |
| 173 | − "type": "text", | |
| 174 | − "typeSchema": null, | |
| 175 | − "notNull": true, | |
| 176 | − "dimensions": 0, | |
| 177 | − "default": null, | |
| 178 | − "generated": null, | |
| 179 | − "identity": null, | |
| 180 | − "name": "id", | |
| 181 | − "entityType": "columns", | |
| 182 | − "schema": "public", | |
| 183 | − "table": "execution_interactions" | |
| 184 | − }, | |
| 185 | − { | |
| 186 | − "type": "text", | |
| 187 | − "typeSchema": null, | |
| 188 | − "notNull": true, | |
| 189 | − "dimensions": 0, | |
| 190 | − "default": null, | |
| 191 | − "generated": null, | |
| 192 | − "identity": null, | |
| 193 | − "name": "execution_id", | |
| 194 | − "entityType": "columns", | |
| 195 | − "schema": "public", | |
| 196 | − "table": "execution_interactions" | |
| 197 | − }, | |
| 198 | − { | |
| 199 | − "type": "text", | |
| 200 | − "typeSchema": null, | |
| 201 | − "notNull": true, | |
| 202 | − "dimensions": 0, | |
| 203 | − "default": null, | |
| 204 | − "generated": null, | |
| 205 | − "identity": null, | |
| 206 | − "name": "status", | |
| 207 | − "entityType": "columns", | |
| 208 | − "schema": "public", | |
| 209 | − "table": "execution_interactions" | |
| 210 | − }, | |
| 211 | − { | |
| 212 | − "type": "text", | |
| 213 | − "typeSchema": null, | |
| 214 | − "notNull": true, | |
| 215 | − "dimensions": 0, | |
| 216 | − "default": null, | |
| 217 | − "generated": null, | |
| 218 | − "identity": null, | |
| 219 | − "name": "kind", | |
| 220 | − "entityType": "columns", | |
| 221 | − "schema": "public", | |
| 222 | − "table": "execution_interactions" | |
| 223 | − }, | |
| 224 | − { | |
| 225 | − "type": "text", | |
| 226 | − "typeSchema": null, | |
| 227 | − "notNull": true, | |
| 228 | − "dimensions": 0, | |
| 229 | − "default": null, | |
| 230 | − "generated": null, | |
| 231 | − "identity": null, | |
| 232 | − "name": "payload_json", | |
| 233 | − "entityType": "columns", | |
| 234 | − "schema": "public", | |
| 235 | − "table": "execution_interactions" | |
| 236 | − }, | |
| 237 | − { | |
| 238 | − "type": "text", | |
| 239 | − "typeSchema": null, | |
| 240 | − "notNull": false, | |
| 241 | − "dimensions": 0, | |
| 242 | − "default": null, | |
| 243 | − "generated": null, | |
| 244 | − "identity": null, | |
| 245 | − "name": "response_json", | |
| 246 | − "entityType": "columns", | |
| 247 | − "schema": "public", | |
| 248 | − "table": "execution_interactions" | |
| 249 | − }, | |
| 250 | − { | |
| 251 | − "type": "bigint", | |
| 252 | − "typeSchema": null, | |
| 253 | − "notNull": true, | |
| 254 | − "dimensions": 0, | |
| 255 | − "default": null, | |
| 256 | − "generated": null, | |
| 257 | − "identity": null, | |
| 258 | − "name": "created_at", | |
| 259 | − "entityType": "columns", | |
| 260 | − "schema": "public", | |
| 261 | − "table": "execution_interactions" | |
| 262 | − }, | |
| 263 | − { | |
| 264 | − "type": "bigint", | |
| 265 | − "typeSchema": null, | |
| 266 | − "notNull": true, | |
| 267 | − "dimensions": 0, | |
| 268 | − "default": null, | |
| 269 | − "generated": null, | |
| 270 | − "identity": null, | |
| 271 | − "name": "updated_at", | |
| 272 | − "entityType": "columns", | |
| 273 | − "schema": "public", | |
| 274 | − "table": "execution_interactions" | |
| 275 | − }, | |
| 276 | − { | |
| 277 | − "type": "text", | |
| 278 | − "typeSchema": null, | |
| 279 | − "notNull": true, | |
| 280 | − "dimensions": 0, | |
| 281 | − "default": null, | |
| 282 | − "generated": null, | |
| 283 | − "identity": null, | |
| 284 | − "name": "id", | |
| 285 | − "entityType": "columns", | |
| 286 | − "schema": "public", | |
| 287 | − "table": "executions" | |
| 288 | − }, | |
| 289 | − { | |
| 290 | − "type": "text", | |
| 291 | − "typeSchema": null, | |
| 292 | − "notNull": true, | |
| 293 | − "dimensions": 0, | |
| 294 | − "default": null, | |
| 295 | − "generated": null, | |
| 296 | − "identity": null, | |
| 297 | − "name": "workspace_id", | |
| 298 | − "entityType": "columns", | |
| 299 | − "schema": "public", | |
| 300 | − "table": "executions" | |
| 301 | − }, | |
| 302 | − { | |
| 303 | − "type": "text", | |
| 304 | − "typeSchema": null, | |
| 305 | − "notNull": true, | |
| 306 | − "dimensions": 0, | |
| 307 | − "default": null, | |
| 308 | − "generated": null, | |
| 309 | − "identity": null, | |
| 310 | − "name": "created_by_account_id", | |
| 311 | − "entityType": "columns", | |
| 312 | − "schema": "public", | |
| 313 | − "table": "executions" | |
| 314 | − }, | |
| 315 | − { | |
| 316 | − "type": "text", | |
| 317 | − "typeSchema": null, | |
| 318 | − "notNull": true, | |
| 319 | − "dimensions": 0, | |
| 320 | − "default": null, | |
| 321 | − "generated": null, | |
| 322 | − "identity": null, | |
| 323 | − "name": "status", | |
| 324 | − "entityType": "columns", | |
| 325 | − "schema": "public", | |
| 326 | − "table": "executions" | |
| 327 | − }, | |
| 328 | − { | |
| 329 | − "type": "text", | |
| 330 | − "typeSchema": null, | |
| 331 | − "notNull": true, | |
| 332 | − "dimensions": 0, | |
| 333 | − "default": null, | |
| 334 | − "generated": null, | |
| 335 | − "identity": null, | |
| 336 | − "name": "code", | |
| 337 | − "entityType": "columns", | |
| 338 | − "schema": "public", | |
| 339 | − "table": "executions" | |
| 340 | − }, | |
| 341 | − { | |
| 342 | − "type": "text", | |
| 343 | − "typeSchema": null, | |
| 344 | − "notNull": false, | |
| 345 | − "dimensions": 0, | |
| 346 | − "default": null, | |
| 347 | − "generated": null, | |
| 348 | − "identity": null, | |
| 349 | − "name": "result_json", | |
| 350 | − "entityType": "columns", | |
| 351 | − "schema": "public", | |
| 352 | − "table": "executions" | |
| 353 | − }, | |
| 354 | − { | |
| 355 | − "type": "text", | |
| 356 | − "typeSchema": null, | |
| 357 | − "notNull": false, | |
| 358 | − "dimensions": 0, | |
| 359 | − "default": null, | |
| 360 | − "generated": null, | |
| 361 | − "identity": null, | |
| 362 | − "name": "error_text", | |
| 363 | − "entityType": "columns", | |
| 364 | − "schema": "public", | |
| 365 | − "table": "executions" | |
| 366 | − }, | |
| 367 | − { | |
| 368 | − "type": "text", | |
| 369 | − "typeSchema": null, | |
| 370 | − "notNull": false, | |
| 371 | − "dimensions": 0, | |
| 372 | − "default": null, | |
| 373 | − "generated": null, | |
| 374 | − "identity": null, | |
| 375 | − "name": "logs_json", | |
| 376 | − "entityType": "columns", | |
| 377 | − "schema": "public", | |
| 378 | − "table": "executions" | |
| 379 | − }, | |
| 380 | − { | |
| 381 | − "type": "bigint", | |
| 382 | − "typeSchema": null, | |
| 383 | − "notNull": false, | |
| 384 | − "dimensions": 0, | |
| 385 | − "default": null, | |
| 386 | − "generated": null, | |
| 387 | − "identity": null, | |
| 388 | − "name": "started_at", | |
| 389 | − "entityType": "columns", | |
| 390 | − "schema": "public", | |
| 391 | − "table": "executions" | |
| 392 | − }, | |
| 393 | − { | |
| 394 | − "type": "bigint", | |
| 395 | − "typeSchema": null, | |
| 396 | − "notNull": false, | |
| 397 | − "dimensions": 0, | |
| 398 | − "default": null, | |
| 399 | − "generated": null, | |
| 400 | − "identity": null, | |
| 401 | − "name": "completed_at", | |
| 402 | − "entityType": "columns", | |
| 403 | − "schema": "public", | |
| 404 | − "table": "executions" | |
| 405 | − }, | |
| 406 | − { | |
| 407 | − "type": "bigint", | |
| 408 | − "typeSchema": null, | |
| 409 | − "notNull": true, | |
| 410 | − "dimensions": 0, | |
| 411 | − "default": null, | |
| 412 | − "generated": null, | |
| 413 | − "identity": null, | |
| 414 | − "name": "created_at", | |
| 415 | − "entityType": "columns", | |
| 416 | − "schema": "public", | |
| 417 | − "table": "executions" | |
| 418 | − }, | |
| 419 | − { | |
| 420 | − "type": "bigint", | |
| 421 | − "typeSchema": null, | |
| 422 | − "notNull": true, | |
| 423 | − "dimensions": 0, | |
| 424 | − "default": null, | |
| 425 | − "generated": null, | |
| 426 | − "identity": null, | |
| 427 | − "name": "updated_at", | |
| 428 | − "entityType": "columns", | |
| 429 | − "schema": "public", | |
| 430 | − "table": "executions" | |
| 431 | − }, | |
| 432 | − { | |
| 433 | − "type": "text", | |
| 434 | − "typeSchema": null, | |
| 435 | − "notNull": true, | |
| 436 | − "dimensions": 0, | |
| 437 | − "default": null, | |
| 438 | − "generated": null, | |
| 439 | − "identity": null, | |
| 440 | − "name": "id", | |
| 441 | − "entityType": "columns", | |
| 442 | − "schema": "public", | |
| 443 | − "table": "local_installations" | |
| 444 | − }, | |
| 445 | − { | |
| 446 | − "type": "text", | |
| 447 | − "typeSchema": null, | |
| 448 | − "notNull": true, | |
| 449 | − "dimensions": 0, | |
| 450 | − "default": null, | |
| 451 | − "generated": null, | |
| 452 | − "identity": null, | |
| 453 | − "name": "account_id", | |
| 454 | − "entityType": "columns", | |
| 455 | − "schema": "public", | |
| 456 | − "table": "local_installations" | |
| 457 | − }, | |
| 458 | − { | |
| 459 | − "type": "text", | |
| 460 | − "typeSchema": null, | |
| 461 | − "notNull": true, | |
| 462 | − "dimensions": 0, | |
| 463 | − "default": null, | |
| 464 | − "generated": null, | |
| 465 | − "identity": null, | |
| 466 | − "name": "organization_id", | |
| 467 | − "entityType": "columns", | |
| 468 | − "schema": "public", | |
| 469 | − "table": "local_installations" | |
| 470 | − }, | |
| 471 | − { | |
| 472 | − "type": "text", | |
| 473 | − "typeSchema": null, | |
| 474 | − "notNull": true, | |
| 475 | − "dimensions": 0, | |
| 476 | − "default": null, | |
| 477 | − "generated": null, | |
| 478 | − "identity": null, | |
| 479 | − "name": "workspace_id", | |
| 480 | − "entityType": "columns", | |
| 481 | − "schema": "public", | |
| 482 | − "table": "local_installations" | |
| 483 | − }, | |
| 484 | − { | |
| 485 | − "type": "bigint", | |
| 486 | − "typeSchema": null, | |
| 487 | − "notNull": true, | |
| 488 | − "dimensions": 0, | |
| 489 | − "default": null, | |
| 490 | − "generated": null, | |
| 491 | − "identity": null, | |
| 492 | − "name": "created_at", | |
| 493 | − "entityType": "columns", | |
| 494 | − "schema": "public", | |
| 495 | − "table": "local_installations" | |
| 496 | − }, | |
| 497 | − { | |
| 498 | − "type": "bigint", | |
| 499 | − "typeSchema": null, | |
| 500 | − "notNull": true, | |
| 501 | − "dimensions": 0, | |
| 502 | − "default": null, | |
| 503 | − "generated": null, | |
| 504 | − "identity": null, | |
| 505 | − "name": "updated_at", | |
| 506 | − "entityType": "columns", | |
| 507 | − "schema": "public", | |
| 508 | − "table": "local_installations" | |
| 509 | − }, | |
| 510 | − { | |
| 511 | − "type": "text", | |
| 512 | − "typeSchema": null, | |
| 513 | − "notNull": true, | |
| 514 | − "dimensions": 0, | |
| 515 | − "default": null, | |
| 516 | − "generated": null, | |
| 517 | − "identity": null, | |
| 518 | − "name": "id", | |
| 519 | − "entityType": "columns", | |
| 520 | − "schema": "public", | |
| 521 | − "table": "organization_memberships" | |
| 522 | − }, | |
| 523 | − { | |
| 524 | − "type": "text", | |
| 525 | − "typeSchema": null, | |
| 526 | − "notNull": true, | |
| 527 | − "dimensions": 0, | |
| 528 | − "default": null, | |
| 529 | − "generated": null, | |
| 530 | − "identity": null, | |
| 531 | − "name": "organization_id", | |
| 532 | − "entityType": "columns", | |
| 533 | − "schema": "public", | |
| 534 | − "table": "organization_memberships" | |
| 535 | − }, | |
| 536 | − { | |
| 537 | − "type": "text", | |
| 538 | − "typeSchema": null, | |
| 539 | − "notNull": true, | |
| 540 | − "dimensions": 0, | |
| 541 | − "default": null, | |
| 542 | − "generated": null, | |
| 543 | − "identity": null, | |
| 544 | − "name": "account_id", | |
| 545 | − "entityType": "columns", | |
| 546 | − "schema": "public", | |
| 547 | − "table": "organization_memberships" | |
| 548 | − }, | |
| 549 | − { | |
| 550 | − "type": "text", | |
| 551 | − "typeSchema": null, | |
| 552 | − "notNull": true, | |
| 553 | − "dimensions": 0, | |
| 554 | − "default": null, | |
| 555 | − "generated": null, | |
| 556 | − "identity": null, | |
| 557 | − "name": "role", | |
| 558 | − "entityType": "columns", | |
| 559 | − "schema": "public", | |
| 560 | − "table": "organization_memberships" | |
| 561 | − }, | |
| 562 | − { | |
| 563 | − "type": "text", | |
| 564 | − "typeSchema": null, | |
| 565 | − "notNull": true, | |
| 566 | − "dimensions": 0, | |
| 567 | − "default": null, | |
| 568 | − "generated": null, | |
| 569 | − "identity": null, | |
| 570 | − "name": "status", | |
| 571 | − "entityType": "columns", | |
| 572 | − "schema": "public", | |
| 573 | − "table": "organization_memberships" | |
| 574 | − }, | |
| 575 | − { | |
| 576 | − "type": "boolean", | |
| 577 | − "typeSchema": null, | |
| 578 | − "notNull": true, | |
| 579 | − "dimensions": 0, | |
| 580 | − "default": null, | |
| 581 | − "generated": null, | |
| 582 | − "identity": null, | |
| 583 | − "name": "billable", | |
| 584 | − "entityType": "columns", | |
| 585 | − "schema": "public", | |
| 586 | − "table": "organization_memberships" | |
| 587 | − }, | |
| 588 | − { | |
| 589 | − "type": "text", | |
| 590 | − "typeSchema": null, | |
| 591 | − "notNull": false, | |
| 592 | − "dimensions": 0, | |
| 593 | − "default": null, | |
| 594 | − "generated": null, | |
| 595 | − "identity": null, | |
| 596 | − "name": "invited_by_account_id", | |
| 597 | − "entityType": "columns", | |
| 598 | − "schema": "public", | |
| 599 | − "table": "organization_memberships" | |
| 600 | − }, | |
| 601 | − { | |
| 602 | − "type": "bigint", | |
| 603 | − "typeSchema": null, | |
| 604 | − "notNull": false, | |
| 605 | − "dimensions": 0, | |
| 606 | − "default": null, | |
| 607 | − "generated": null, | |
| 608 | − "identity": null, | |
| 609 | − "name": "joined_at", | |
| 610 | − "entityType": "columns", | |
| 611 | − "schema": "public", | |
| 612 | − "table": "organization_memberships" | |
| 613 | − }, | |
| 614 | − { | |
| 615 | − "type": "bigint", | |
| 616 | − "typeSchema": null, | |
| 617 | − "notNull": true, | |
| 618 | − "dimensions": 0, | |
| 619 | − "default": null, | |
| 620 | − "generated": null, | |
| 621 | − "identity": null, | |
| 622 | − "name": "created_at", | |
| 623 | − "entityType": "columns", | |
| 624 | − "schema": "public", | |
| 625 | − "table": "organization_memberships" | |
| 626 | − }, | |
| 627 | − { | |
| 628 | − "type": "bigint", | |
| 629 | − "typeSchema": null, | |
| 630 | − "notNull": true, | |
| 631 | − "dimensions": 0, | |
| 632 | − "default": null, | |
| 633 | − "generated": null, | |
| 634 | − "identity": null, | |
| 635 | − "name": "updated_at", | |
| 636 | − "entityType": "columns", | |
| 637 | − "schema": "public", | |
| 638 | − "table": "organization_memberships" | |
| 639 | − }, | |
| 640 | − { | |
| 641 | − "type": "text", | |
| 642 | − "typeSchema": null, | |
| 643 | − "notNull": true, | |
| 644 | − "dimensions": 0, | |
| 645 | − "default": null, | |
| 646 | − "generated": null, | |
| 647 | − "identity": null, | |
| 648 | − "name": "id", | |
| 649 | − "entityType": "columns", | |
| 650 | − "schema": "public", | |
| 651 | − "table": "organizations" | |
| 652 | − }, | |
| 653 | − { | |
| 654 | − "type": "text", | |
| 655 | − "typeSchema": null, | |
| 656 | − "notNull": true, | |
| 657 | − "dimensions": 0, | |
| 658 | − "default": null, | |
| 659 | − "generated": null, | |
| 660 | − "identity": null, | |
| 661 | − "name": "slug", | |
| 662 | − "entityType": "columns", | |
| 663 | − "schema": "public", | |
| 664 | − "table": "organizations" | |
| 665 | − }, | |
| 666 | − { | |
| 667 | − "type": "text", | |
| 668 | − "typeSchema": null, | |
| 669 | − "notNull": true, | |
| 670 | − "dimensions": 0, | |
| 671 | − "default": null, | |
| 672 | − "generated": null, | |
| 673 | − "identity": null, | |
| 674 | − "name": "name", | |
| 675 | − "entityType": "columns", | |
| 676 | − "schema": "public", | |
| 677 | − "table": "organizations" | |
| 678 | − }, | |
| 679 | − { | |
| 680 | − "type": "text", | |
| 681 | − "typeSchema": null, | |
| 682 | − "notNull": true, | |
| 683 | − "dimensions": 0, | |
| 684 | − "default": null, | |
| 685 | − "generated": null, | |
| 686 | − "identity": null, | |
| 687 | − "name": "status", | |
| 688 | − "entityType": "columns", | |
| 689 | − "schema": "public", | |
| 690 | − "table": "organizations" | |
| 691 | − }, | |
| 692 | − { | |
| 693 | − "type": "text", | |
| 694 | − "typeSchema": null, | |
| 695 | − "notNull": false, | |
| 696 | − "dimensions": 0, | |
| 697 | − "default": null, | |
| 698 | − "generated": null, | |
| 699 | − "identity": null, | |
| 700 | − "name": "created_by_account_id", | |
| 701 | − "entityType": "columns", | |
| 702 | − "schema": "public", | |
| 703 | − "table": "organizations" | |
| 704 | − }, | |
| 705 | − { | |
| 706 | − "type": "bigint", | |
| 707 | − "typeSchema": null, | |
| 708 | − "notNull": true, | |
| 709 | − "dimensions": 0, | |
| 710 | − "default": null, | |
| 711 | − "generated": null, | |
| 712 | − "identity": null, | |
| 713 | − "name": "created_at", | |
| 714 | − "entityType": "columns", | |
| 715 | − "schema": "public", | |
| 716 | − "table": "organizations" | |
| 717 | − }, | |
| 718 | − { | |
| 719 | − "type": "bigint", | |
| 720 | − "typeSchema": null, | |
| 721 | − "notNull": true, | |
| 722 | − "dimensions": 0, | |
| 723 | − "default": null, | |
| 724 | − "generated": null, | |
| 725 | − "identity": null, | |
| 726 | − "name": "updated_at", | |
| 727 | − "entityType": "columns", | |
| 728 | − "schema": "public", | |
| 729 | − "table": "organizations" | |
| 730 | − }, | |
| 731 | − { | |
| 732 | − "type": "text", | |
| 733 | − "typeSchema": null, | |
| 734 | − "notNull": true, | |
| 735 | − "dimensions": 0, | |
| 736 | − "default": null, | |
| 737 | − "generated": null, | |
| 738 | − "identity": null, | |
| 739 | − "name": "id", | |
| 740 | − "entityType": "columns", | |
| 741 | − "schema": "public", | |
| 742 | − "table": "policies" | |
| 743 | − }, | |
| 744 | − { | |
| 745 | − "type": "text", | |
| 746 | − "typeSchema": null, | |
| 747 | − "notNull": true, | |
| 748 | − "dimensions": 0, | |
| 749 | − "default": null, | |
| 750 | − "generated": null, | |
| 751 | − "identity": null, | |
| 752 | − "name": "workspace_id", | |
| 753 | − "entityType": "columns", | |
| 754 | − "schema": "public", | |
| 755 | − "table": "policies" | |
| 756 | − }, | |
| 757 | − { | |
| 758 | − "type": "text", | |
| 759 | − "typeSchema": null, | |
| 760 | − "notNull": false, | |
| 761 | − "dimensions": 0, | |
| 762 | − "default": null, | |
| 763 | − "generated": null, | |
| 764 | − "identity": null, | |
| 765 | − "name": "target_account_id", | |
| 766 | − "entityType": "columns", | |
| 767 | − "schema": "public", | |
| 768 | − "table": "policies" | |
| 769 | − }, | |
| 770 | − { | |
| 771 | − "type": "text", | |
| 772 | − "typeSchema": null, | |
| 773 | − "notNull": false, | |
| 774 | − "dimensions": 0, | |
| 775 | − "default": null, | |
| 776 | − "generated": null, | |
| 777 | − "identity": null, | |
| 778 | − "name": "client_id", | |
| 779 | − "entityType": "columns", | |
| 780 | − "schema": "public", | |
| 781 | − "table": "policies" | |
| 782 | − }, | |
| 783 | − { | |
| 784 | − "type": "text", | |
| 785 | − "typeSchema": null, | |
| 786 | − "notNull": true, | |
| 787 | − "dimensions": 0, | |
| 788 | − "default": null, | |
| 789 | − "generated": null, | |
| 790 | − "identity": null, | |
| 791 | − "name": "resource_type", | |
| 792 | − "entityType": "columns", | |
| 793 | − "schema": "public", | |
| 794 | − "table": "policies" | |
| 795 | − }, | |
| 796 | − { | |
| 797 | − "type": "text", | |
| 798 | − "typeSchema": null, | |
| 799 | − "notNull": true, | |
| 800 | − "dimensions": 0, | |
| 801 | − "default": null, | |
| 802 | − "generated": null, | |
| 803 | − "identity": null, | |
| 804 | − "name": "resource_pattern", | |
| 805 | − "entityType": "columns", | |
| 806 | − "schema": "public", | |
| 807 | − "table": "policies" | |
| 808 | − }, | |
| 809 | − { | |
| 810 | − "type": "text", | |
| 811 | − "typeSchema": null, | |
| 812 | − "notNull": true, | |
| 813 | − "dimensions": 0, | |
| 814 | − "default": null, | |
| 815 | − "generated": null, | |
| 816 | − "identity": null, | |
| 817 | − "name": "match_type", | |
| 818 | − "entityType": "columns", | |
| 819 | − "schema": "public", | |
| 820 | − "table": "policies" | |
| 821 | − }, | |
| 822 | − { | |
| 823 | − "type": "text", | |
| 824 | − "typeSchema": null, | |
| 825 | − "notNull": true, | |
| 826 | − "dimensions": 0, | |
| 827 | − "default": null, | |
| 828 | − "generated": null, | |
| 829 | − "identity": null, | |
| 830 | − "name": "effect", | |
| 831 | − "entityType": "columns", | |
| 832 | − "schema": "public", | |
| 833 | − "table": "policies" | |
| 834 | − }, | |
| 835 | − { | |
| 836 | − "type": "text", | |
| 837 | − "typeSchema": null, | |
| 838 | − "notNull": true, | |
| 839 | − "dimensions": 0, | |
| 840 | − "default": null, | |
| 841 | − "generated": null, | |
| 842 | − "identity": null, | |
| 843 | − "name": "approval_mode", | |
| 844 | − "entityType": "columns", | |
| 845 | − "schema": "public", | |
| 846 | − "table": "policies" | |
| 847 | − }, | |
| 848 | − { | |
| 849 | − "type": "text", | |
| 850 | − "typeSchema": null, | |
| 851 | − "notNull": false, | |
| 852 | − "dimensions": 0, | |
| 853 | − "default": null, | |
| 854 | − "generated": null, | |
| 855 | − "identity": null, | |
| 856 | − "name": "argument_conditions_json", | |
| 857 | − "entityType": "columns", | |
| 858 | − "schema": "public", | |
| 859 | − "table": "policies" | |
| 860 | − }, | |
| 861 | − { | |
| 862 | − "type": "bigint", | |
| 863 | − "typeSchema": null, | |
| 864 | − "notNull": true, | |
| 865 | − "dimensions": 0, | |
| 866 | − "default": null, | |
| 867 | − "generated": null, | |
| 868 | − "identity": null, | |
| 869 | − "name": "priority", | |
| 870 | − "entityType": "columns", | |
| 871 | − "schema": "public", | |
| 872 | − "table": "policies" | |
| 873 | − }, | |
| 874 | − { | |
| 875 | − "type": "boolean", | |
| 876 | − "typeSchema": null, | |
| 877 | − "notNull": true, | |
| 878 | − "dimensions": 0, | |
| 879 | − "default": null, | |
| 880 | − "generated": null, | |
| 881 | − "identity": null, | |
| 882 | − "name": "enabled", | |
| 883 | − "entityType": "columns", | |
| 884 | − "schema": "public", | |
| 885 | − "table": "policies" | |
| 886 | − }, | |
| 887 | − { | |
| 888 | − "type": "bigint", | |
| 889 | − "typeSchema": null, | |
| 890 | − "notNull": true, | |
| 891 | − "dimensions": 0, | |
| 892 | − "default": null, | |
| 893 | − "generated": null, | |
| 894 | − "identity": null, | |
| 895 | − "name": "created_at", | |
| 896 | − "entityType": "columns", | |
| 897 | − "schema": "public", | |
| 898 | − "table": "policies" | |
| 899 | − }, | |
| 900 | − { | |
| 901 | − "type": "bigint", | |
| 902 | − "typeSchema": null, | |
| 903 | − "notNull": true, | |
| 904 | − "dimensions": 0, | |
| 905 | − "default": null, | |
| 906 | − "generated": null, | |
| 907 | − "identity": null, | |
| 908 | − "name": "updated_at", | |
| 909 | − "entityType": "columns", | |
| 910 | − "schema": "public", | |
| 911 | − "table": "policies" | |
| 912 | − }, | |
| 913 | − { | |
| 914 | − "type": "text", | |
| 915 | − "typeSchema": null, | |
| 916 | − "notNull": true, | |
| 917 | − "dimensions": 0, | |
| 918 | − "default": null, | |
| 919 | − "generated": null, | |
| 920 | − "identity": null, | |
| 921 | − "name": "id", | |
| 922 | − "entityType": "columns", | |
| 923 | − "schema": "public", | |
| 924 | − "table": "secret_materials" | |
| 925 | − }, | |
| 926 | − { | |
| 927 | − "type": "text", | |
| 928 | − "typeSchema": null, | |
| 929 | − "notNull": true, | |
| 930 | − "dimensions": 0, | |
| 931 | − "default": null, | |
| 932 | − "generated": null, | |
| 933 | − "identity": null, | |
| 934 | − "name": "purpose", | |
| 935 | − "entityType": "columns", | |
| 936 | − "schema": "public", | |
| 937 | − "table": "secret_materials" | |
| 938 | − }, | |
| 939 | − { | |
| 940 | − "type": "text", | |
| 941 | − "typeSchema": null, | |
| 942 | − "notNull": true, | |
| 943 | − "dimensions": 0, | |
| 944 | − "default": null, | |
| 945 | − "generated": null, | |
| 946 | − "identity": null, | |
| 947 | − "name": "value", | |
| 948 | − "entityType": "columns", | |
| 949 | − "schema": "public", | |
| 950 | − "table": "secret_materials" | |
| 951 | − }, | |
| 952 | − { | |
| 953 | − "type": "bigint", | |
| 954 | − "typeSchema": null, | |
| 955 | − "notNull": true, | |
| 956 | − "dimensions": 0, | |
| 957 | − "default": null, | |
| 958 | − "generated": null, | |
| 959 | − "identity": null, | |
| 960 | − "name": "created_at", | |
| 961 | − "entityType": "columns", | |
| 962 | − "schema": "public", | |
| 963 | − "table": "secret_materials" | |
| 964 | − }, | |
| 965 | − { | |
| 966 | − "type": "bigint", | |
| 967 | − "typeSchema": null, | |
| 968 | − "notNull": true, | |
| 969 | − "dimensions": 0, | |
| 970 | − "default": null, | |
| 971 | − "generated": null, | |
| 972 | − "identity": null, | |
| 973 | − "name": "updated_at", | |
| 974 | − "entityType": "columns", | |
| 975 | − "schema": "public", | |
| 976 | − "table": "secret_materials" | |
| 977 | − }, | |
| 978 | − { | |
| 979 | − "type": "text", | |
| 980 | − "typeSchema": null, | |
| 981 | − "notNull": true, | |
| 982 | − "dimensions": 0, | |
| 983 | − "default": null, | |
| 984 | − "generated": null, | |
| 985 | − "identity": null, | |
| 986 | − "name": "id", | |
| 987 | − "entityType": "columns", | |
| 988 | − "schema": "public", | |
| 989 | − "table": "source_auth_sessions" | |
| 990 | − }, | |
| 991 | − { | |
| 992 | − "type": "text", | |
| 993 | − "typeSchema": null, | |
| 994 | − "notNull": true, | |
| 995 | − "dimensions": 0, | |
| 996 | − "default": null, | |
| 997 | − "generated": null, | |
| 998 | − "identity": null, | |
| 999 | − "name": "workspace_id", | |
| 1000 | − "entityType": "columns", | |
| 1001 | − "schema": "public", | |
| 1002 | − "table": "source_auth_sessions" | |
| 1003 | − }, | |
| 1004 | − { | |
| 1005 | − "type": "text", | |
| 1006 | − "typeSchema": null, | |
| 1007 | − "notNull": true, | |
| 1008 | − "dimensions": 0, | |
| 1009 | − "default": null, | |
| 1010 | − "generated": null, | |
| 1011 | − "identity": null, | |
| 1012 | − "name": "source_id", | |
| 1013 | − "entityType": "columns", | |
| 1014 | − "schema": "public", | |
| 1015 | − "table": "source_auth_sessions" | |
| 1016 | − }, | |
| 1017 | − { | |
| 1018 | − "type": "text", | |
| 1019 | − "typeSchema": null, | |
| 1020 | − "notNull": false, | |
| 1021 | − "dimensions": 0, | |
| 1022 | − "default": null, | |
| 1023 | − "generated": null, | |
| 1024 | − "identity": null, | |
| 1025 | − "name": "execution_id", | |
| 1026 | − "entityType": "columns", | |
| 1027 | − "schema": "public", | |
| 1028 | − "table": "source_auth_sessions" | |
| 1029 | − }, | |
| 1030 | − { | |
| 1031 | − "type": "text", | |
| 1032 | − "typeSchema": null, | |
| 1033 | − "notNull": false, | |
| 1034 | − "dimensions": 0, | |
| 1035 | − "default": null, | |
| 1036 | − "generated": null, | |
| 1037 | − "identity": null, | |
| 1038 | − "name": "interaction_id", | |
| 1039 | − "entityType": "columns", | |
| 1040 | − "schema": "public", | |
| 1041 | − "table": "source_auth_sessions" | |
| 1042 | − }, | |
| 1043 | − { | |
| 1044 | − "type": "text", | |
| 1045 | − "typeSchema": null, | |
| 1046 | − "notNull": true, | |
| 1047 | − "dimensions": 0, | |
| 1048 | − "default": null, | |
| 1049 | − "generated": null, | |
| 1050 | − "identity": null, | |
| 1051 | − "name": "strategy", | |
| 1052 | − "entityType": "columns", | |
| 1053 | − "schema": "public", | |
| 1054 | − "table": "source_auth_sessions" | |
| 1055 | − }, | |
| 1056 | − { | |
| 1057 | − "type": "text", | |
| 1058 | − "typeSchema": null, | |
| 1059 | − "notNull": true, | |
| 1060 | − "dimensions": 0, | |
| 1061 | − "default": null, | |
| 1062 | − "generated": null, | |
| 1063 | − "identity": null, | |
| 1064 | − "name": "status", | |
| 1065 | − "entityType": "columns", | |
| 1066 | − "schema": "public", | |
| 1067 | − "table": "source_auth_sessions" | |
| 1068 | − }, | |
| 1069 | − { | |
| 1070 | − "type": "text", | |
| 1071 | − "typeSchema": null, | |
| 1072 | − "notNull": true, | |
| 1073 | − "dimensions": 0, | |
| 1074 | − "default": null, | |
| 1075 | − "generated": null, | |
| 1076 | − "identity": null, | |
| 1077 | − "name": "endpoint", | |
| 1078 | − "entityType": "columns", | |
| 1079 | − "schema": "public", | |
| 1080 | − "table": "source_auth_sessions" | |
| 1081 | − }, | |
| 1082 | − { | |
| 1083 | − "type": "text", | |
| 1084 | − "typeSchema": null, | |
| 1085 | − "notNull": true, | |
| 1086 | − "dimensions": 0, | |
| 1087 | − "default": null, | |
| 1088 | − "generated": null, | |
| 1089 | − "identity": null, | |
| 1090 | − "name": "state", | |
| 1091 | − "entityType": "columns", | |
| 1092 | − "schema": "public", | |
| 1093 | − "table": "source_auth_sessions" | |
| 1094 | − }, | |
| 1095 | − { | |
| 1096 | − "type": "text", | |
| 1097 | − "typeSchema": null, | |
| 1098 | − "notNull": true, | |
| 1099 | − "dimensions": 0, | |
| 1100 | − "default": null, | |
| 1101 | − "generated": null, | |
| 1102 | − "identity": null, | |
| 1103 | − "name": "redirect_uri", | |
| 1104 | − "entityType": "columns", | |
| 1105 | − "schema": "public", | |
| 1106 | − "table": "source_auth_sessions" | |
| 1107 | − }, | |
| 1108 | − { | |
| 1109 | − "type": "text", | |
| 1110 | − "typeSchema": null, | |
| 1111 | − "notNull": false, | |
| 1112 | − "dimensions": 0, | |
| 1113 | − "default": null, | |
| 1114 | − "generated": null, | |
| 1115 | − "identity": null, | |
| 1116 | − "name": "scope", | |
| 1117 | − "entityType": "columns", | |
| 1118 | − "schema": "public", | |
| 1119 | − "table": "source_auth_sessions" | |
| 1120 | − }, | |
| 1121 | − { | |
| 1122 | − "type": "text", | |
| 1123 | − "typeSchema": null, | |
| 1124 | − "notNull": false, | |
| 1125 | − "dimensions": 0, | |
| 1126 | − "default": null, | |
| 1127 | − "generated": null, | |
| 1128 | − "identity": null, | |
| 1129 | − "name": "resource_metadata_url", | |
| 1130 | − "entityType": "columns", | |
| 1131 | − "schema": "public", | |
| 1132 | − "table": "source_auth_sessions" | |
| 1133 | − }, | |
| 1134 | − { | |
| 1135 | − "type": "text", | |
| 1136 | − "typeSchema": null, | |
| 1137 | − "notNull": false, | |
| 1138 | − "dimensions": 0, | |
| 1139 | − "default": null, | |
| 1140 | − "generated": null, | |
| 1141 | − "identity": null, | |
| 1142 | − "name": "authorization_server_url", | |
| 1143 | − "entityType": "columns", | |
| 1144 | − "schema": "public", | |
| 1145 | − "table": "source_auth_sessions" | |
| 1146 | − }, | |
| 1147 | − { | |
| 1148 | − "type": "text", | |
| 1149 | − "typeSchema": null, | |
| 1150 | − "notNull": false, | |
| 1151 | − "dimensions": 0, | |
| 1152 | − "default": null, | |
| 1153 | − "generated": null, | |
| 1154 | − "identity": null, | |
| 1155 | − "name": "resource_metadata_json", | |
| 1156 | − "entityType": "columns", | |
| 1157 | − "schema": "public", | |
| 1158 | − "table": "source_auth_sessions" | |
| 1159 | − }, | |
| 1160 | − { | |
| 1161 | − "type": "text", | |
| 1162 | − "typeSchema": null, | |
| 1163 | − "notNull": false, | |
| 1164 | − "dimensions": 0, | |
| 1165 | − "default": null, | |
| 1166 | − "generated": null, | |
| 1167 | − "identity": null, | |
| 1168 | − "name": "authorization_server_metadata_json", | |
| 1169 | − "entityType": "columns", | |
| 1170 | − "schema": "public", | |
| 1171 | − "table": "source_auth_sessions" | |
| 1172 | − }, | |
| 1173 | − { | |
| 1174 | − "type": "text", | |
| 1175 | − "typeSchema": null, | |
| 1176 | − "notNull": false, | |
| 1177 | − "dimensions": 0, | |
| 1178 | − "default": null, | |
| 1179 | − "generated": null, | |
| 1180 | − "identity": null, | |
| 1181 | − "name": "client_information_json", | |
| 1182 | − "entityType": "columns", | |
| 1183 | − "schema": "public", | |
| 1184 | − "table": "source_auth_sessions" | |
| 1185 | − }, | |
| 1186 | − { | |
| 1187 | − "type": "text", | |
| 1188 | − "typeSchema": null, | |
| 1189 | − "notNull": false, | |
| 1190 | − "dimensions": 0, | |
| 1191 | − "default": null, | |
| 1192 | − "generated": null, | |
| 1193 | − "identity": null, | |
| 1194 | − "name": "code_verifier", | |
| 1195 | − "entityType": "columns", | |
| 1196 | − "schema": "public", | |
| 1197 | − "table": "source_auth_sessions" | |
| 1198 | − }, | |
| 1199 | − { | |
| 1200 | − "type": "text", | |
| 1201 | − "typeSchema": null, | |
| 1202 | − "notNull": false, | |
| 1203 | − "dimensions": 0, | |
| 1204 | − "default": null, | |
| 1205 | − "generated": null, | |
| 1206 | − "identity": null, | |
| 1207 | − "name": "authorization_url", | |
| 1208 | − "entityType": "columns", | |
| 1209 | − "schema": "public", | |
| 1210 | − "table": "source_auth_sessions" | |
| 1211 | − }, | |
| 1212 | − { | |
| 1213 | − "type": "text", | |
| 1214 | − "typeSchema": null, | |
| 1215 | − "notNull": false, | |
| 1216 | − "dimensions": 0, | |
| 1217 | − "default": null, | |
| 1218 | − "generated": null, | |
| 1219 | − "identity": null, | |
| 1220 | − "name": "error_text", | |
| 1221 | − "entityType": "columns", | |
| 1222 | − "schema": "public", | |
| 1223 | − "table": "source_auth_sessions" | |
| 1224 | − }, | |
| 1225 | − { | |
| 1226 | − "type": "bigint", | |
| 1227 | − "typeSchema": null, | |
| 1228 | − "notNull": false, | |
| 1229 | − "dimensions": 0, | |
| 1230 | − "default": null, | |
| 1231 | − "generated": null, | |
| 1232 | − "identity": null, | |
| 1233 | − "name": "completed_at", | |
| 1234 | − "entityType": "columns", | |
| 1235 | − "schema": "public", | |
| 1236 | − "table": "source_auth_sessions" | |
| 1237 | − }, | |
| 1238 | − { | |
| 1239 | − "type": "bigint", | |
| 1240 | − "typeSchema": null, | |
| 1241 | − "notNull": true, | |
| 1242 | − "dimensions": 0, | |
| 1243 | − "default": null, | |
| 1244 | − "generated": null, | |
| 1245 | − "identity": null, | |
| 1246 | − "name": "created_at", | |
| 1247 | − "entityType": "columns", | |
| 1248 | − "schema": "public", | |
| 1249 | − "table": "source_auth_sessions" | |
| 1250 | − }, | |
| 1251 | − { | |
| 1252 | − "type": "bigint", | |
| 1253 | − "typeSchema": null, | |
| 1254 | − "notNull": true, | |
| 1255 | − "dimensions": 0, | |
| 1256 | − "default": null, | |
| 1257 | − "generated": null, | |
| 1258 | − "identity": null, | |
| 1259 | − "name": "updated_at", | |
| 1260 | − "entityType": "columns", | |
| 1261 | − "schema": "public", | |
| 1262 | − "table": "source_auth_sessions" | |
| 1263 | − }, | |
| 1264 | − { | |
| 1265 | − "type": "text", | |
| 1266 | − "typeSchema": null, | |
| 1267 | − "notNull": true, | |
| 1268 | − "dimensions": 0, | |
| 1269 | − "default": null, | |
| 1270 | − "generated": null, | |
| 1271 | − "identity": null, | |
| 1272 | − "name": "workspace_id", | |
| 1273 | − "entityType": "columns", | |
| 1274 | − "schema": "public", | |
| 1275 | − "table": "source_credential_bindings" | |
| 1276 | − }, | |
| 1277 | − { | |
| 1278 | − "type": "text", | |
| 1279 | − "typeSchema": null, | |
| 1280 | − "notNull": true, | |
| 1281 | − "dimensions": 0, | |
| 1282 | − "default": null, | |
| 1283 | − "generated": null, | |
| 1284 | − "identity": null, | |
| 1285 | − "name": "source_id", | |
| 1286 | − "entityType": "columns", | |
| 1287 | − "schema": "public", | |
| 1288 | − "table": "source_credential_bindings" | |
| 1289 | − }, | |
| 1290 | − { | |
| 1291 | − "type": "text", | |
| 1292 | − "typeSchema": null, | |
| 1293 | − "notNull": false, | |
| 1294 | − "dimensions": 0, | |
| 1295 | − "default": null, | |
| 1296 | − "generated": null, | |
| 1297 | − "identity": null, | |
| 1298 | − "name": "token_provider_id", | |
| 1299 | − "entityType": "columns", | |
| 1300 | − "schema": "public", | |
| 1301 | − "table": "source_credential_bindings" | |
| 1302 | − }, | |
| 1303 | − { | |
| 1304 | − "type": "text", | |
| 1305 | − "typeSchema": null, | |
| 1306 | − "notNull": false, | |
| 1307 | − "dimensions": 0, | |
| 1308 | − "default": null, | |
| 1309 | − "generated": null, | |
| 1310 | − "identity": null, | |
| 1311 | − "name": "token_handle", | |
| 1312 | − "entityType": "columns", | |
| 1313 | − "schema": "public", | |
| 1314 | − "table": "source_credential_bindings" | |
| 1315 | − }, | |
| 1316 | − { | |
| 1317 | − "type": "text", | |
| 1318 | − "typeSchema": null, | |
| 1319 | − "notNull": false, | |
| 1320 | − "dimensions": 0, | |
| 1321 | − "default": null, | |
| 1322 | − "generated": null, | |
| 1323 | − "identity": null, | |
| 1324 | − "name": "refresh_token_provider_id", | |
| 1325 | − "entityType": "columns", | |
| 1326 | − "schema": "public", | |
| 1327 | − "table": "source_credential_bindings" | |
| 1328 | − }, | |
| 1329 | − { | |
| 1330 | − "type": "text", | |
| 1331 | − "typeSchema": null, | |
| 1332 | − "notNull": false, | |
| 1333 | − "dimensions": 0, | |
| 1334 | − "default": null, | |
| 1335 | − "generated": null, | |
| 1336 | − "identity": null, | |
| 1337 | − "name": "refresh_token_handle", | |
| 1338 | − "entityType": "columns", | |
| 1339 | − "schema": "public", | |
| 1340 | − "table": "source_credential_bindings" | |
| 1341 | − }, | |
| 1342 | − { | |
| 1343 | − "type": "bigint", | |
| 1344 | − "typeSchema": null, | |
| 1345 | − "notNull": true, | |
| 1346 | − "dimensions": 0, | |
| 1347 | − "default": null, | |
| 1348 | − "generated": null, | |
| 1349 | − "identity": null, | |
| 1350 | − "name": "created_at", | |
| 1351 | − "entityType": "columns", | |
| 1352 | − "schema": "public", | |
| 1353 | − "table": "source_credential_bindings" | |
| 1354 | − }, | |
| 1355 | − { | |
| 1356 | − "type": "bigint", | |
| 1357 | − "typeSchema": null, | |
| 1358 | − "notNull": true, | |
| 1359 | − "dimensions": 0, | |
| 1360 | − "default": null, | |
| 1361 | − "generated": null, | |
| 1362 | − "identity": null, | |
| 1363 | − "name": "updated_at", | |
| 1364 | − "entityType": "columns", | |
| 1365 | − "schema": "public", | |
| 1366 | − "table": "source_credential_bindings" | |
| 1367 | − }, | |
| 1368 | − { | |
| 1369 | − "type": "text", | |
| 1370 | − "typeSchema": null, | |
| 1371 | − "notNull": true, | |
| 1372 | − "dimensions": 0, | |
| 1373 | − "default": null, | |
| 1374 | − "generated": null, | |
| 1375 | − "identity": null, | |
| 1376 | − "name": "workspace_id", | |
| 1377 | − "entityType": "columns", | |
| 1378 | − "schema": "public", | |
| 1379 | − "table": "sources" | |
| 1380 | − }, | |
| 1381 | − { | |
| 1382 | − "type": "text", | |
| 1383 | − "typeSchema": null, | |
| 1384 | − "notNull": true, | |
| 1385 | − "dimensions": 0, | |
| 1386 | − "default": null, | |
| 1387 | − "generated": null, | |
| 1388 | − "identity": null, | |
| 1389 | − "name": "source_id", | |
| 1390 | − "entityType": "columns", | |
| 1391 | − "schema": "public", | |
| 1392 | − "table": "sources" | |
| 1393 | − }, | |
| 1394 | − { | |
| 1395 | − "type": "text", | |
| 1396 | − "typeSchema": null, | |
| 1397 | − "notNull": true, | |
| 1398 | − "dimensions": 0, | |
| 1399 | − "default": null, | |
| 1400 | − "generated": null, | |
| 1401 | − "identity": null, | |
| 1402 | − "name": "name", | |
| 1403 | − "entityType": "columns", | |
| 1404 | − "schema": "public", | |
| 1405 | − "table": "sources" | |
| 1406 | − }, | |
| 1407 | − { | |
| 1408 | − "type": "text", | |
| 1409 | − "typeSchema": null, | |
| 1410 | − "notNull": true, | |
| 1411 | − "dimensions": 0, | |
| 1412 | − "default": null, | |
| 1413 | − "generated": null, | |
| 1414 | − "identity": null, | |
| 1415 | − "name": "kind", | |
| 1416 | − "entityType": "columns", | |
| 1417 | − "schema": "public", | |
| 1418 | − "table": "sources" | |
| 1419 | − }, | |
| 1420 | − { | |
| 1421 | − "type": "text", | |
| 1422 | − "typeSchema": null, | |
| 1423 | − "notNull": true, | |
| 1424 | − "dimensions": 0, | |
| 1425 | − "default": null, | |
| 1426 | − "generated": null, | |
| 1427 | − "identity": null, | |
| 1428 | − "name": "endpoint", | |
| 1429 | − "entityType": "columns", | |
| 1430 | − "schema": "public", | |
| 1431 | − "table": "sources" | |
| 1432 | − }, | |
| 1433 | − { | |
| 1434 | − "type": "text", | |
| 1435 | − "typeSchema": null, | |
| 1436 | − "notNull": true, | |
| 1437 | − "dimensions": 0, | |
| 1438 | − "default": null, | |
| 1439 | − "generated": null, | |
| 1440 | − "identity": null, | |
| 1441 | − "name": "status", | |
| 1442 | − "entityType": "columns", | |
| 1443 | − "schema": "public", | |
| 1444 | − "table": "sources" | |
| 1445 | − }, | |
| 1446 | − { | |
| 1447 | − "type": "boolean", | |
| 1448 | − "typeSchema": null, | |
| 1449 | − "notNull": true, | |
| 1450 | − "dimensions": 0, | |
| 1451 | − "default": null, | |
| 1452 | − "generated": null, | |
| 1453 | − "identity": null, | |
| 1454 | − "name": "enabled", | |
| 1455 | − "entityType": "columns", | |
| 1456 | − "schema": "public", | |
| 1457 | − "table": "sources" | |
| 1458 | − }, | |
| 1459 | − { | |
| 1460 | − "type": "text", | |
| 1461 | − "typeSchema": null, | |
| 1462 | − "notNull": false, | |
| 1463 | − "dimensions": 0, | |
| 1464 | − "default": null, | |
| 1465 | − "generated": null, | |
| 1466 | − "identity": null, | |
| 1467 | − "name": "namespace", | |
| 1468 | − "entityType": "columns", | |
| 1469 | − "schema": "public", | |
| 1470 | − "table": "sources" | |
| 1471 | − }, | |
| 1472 | − { | |
| 1473 | − "type": "text", | |
| 1474 | − "typeSchema": null, | |
| 1475 | − "notNull": false, | |
| 1476 | − "dimensions": 0, | |
| 1477 | − "default": null, | |
| 1478 | − "generated": null, | |
| 1479 | − "identity": null, | |
| 1480 | − "name": "transport", | |
| 1481 | − "entityType": "columns", | |
| 1482 | − "schema": "public", | |
| 1483 | − "table": "sources" | |
| 1484 | − }, | |
| 1485 | − { | |
| 1486 | − "type": "text", | |
| 1487 | − "typeSchema": null, | |
| 1488 | − "notNull": false, | |
| 1489 | − "dimensions": 0, | |
| 1490 | − "default": null, | |
| 1491 | − "generated": null, | |
| 1492 | − "identity": null, | |
| 1493 | − "name": "query_params_json", | |
| 1494 | − "entityType": "columns", | |
| 1495 | − "schema": "public", | |
| 1496 | − "table": "sources" | |
| 1497 | − }, | |
| 1498 | − { | |
| 1499 | − "type": "text", | |
| 1500 | − "typeSchema": null, | |
| 1501 | − "notNull": false, | |
| 1502 | − "dimensions": 0, | |
| 1503 | − "default": null, | |
| 1504 | − "generated": null, | |
| 1505 | − "identity": null, | |
| 1506 | − "name": "headers_json", | |
| 1507 | − "entityType": "columns", | |
| 1508 | − "schema": "public", | |
| 1509 | − "table": "sources" | |
| 1510 | − }, | |
| 1511 | − { | |
| 1512 | − "type": "text", | |
| 1513 | − "typeSchema": null, | |
| 1514 | − "notNull": false, | |
| 1515 | − "dimensions": 0, | |
| 1516 | − "default": null, | |
| 1517 | − "generated": null, | |
| 1518 | − "identity": null, | |
| 1519 | − "name": "spec_url", | |
| 1520 | − "entityType": "columns", | |
| 1521 | − "schema": "public", | |
| 1522 | − "table": "sources" | |
| 1523 | − }, | |
| 1524 | − { | |
| 1525 | − "type": "text", | |
| 1526 | − "typeSchema": null, | |
| 1527 | − "notNull": false, | |
| 1528 | − "dimensions": 0, | |
| 1529 | − "default": null, | |
| 1530 | − "generated": null, | |
| 1531 | − "identity": null, | |
| 1532 | − "name": "default_headers_json", | |
| 1533 | − "entityType": "columns", | |
| 1534 | − "schema": "public", | |
| 1535 | − "table": "sources" | |
| 1536 | − }, | |
| 1537 | − { | |
| 1538 | − "type": "text", | |
| 1539 | − "typeSchema": null, | |
| 1540 | − "notNull": true, | |
| 1541 | − "dimensions": 0, | |
| 1542 | − "default": null, | |
| 1543 | − "generated": null, | |
| 1544 | − "identity": null, | |
| 1545 | − "name": "auth_kind", | |
| 1546 | − "entityType": "columns", | |
| 1547 | − "schema": "public", | |
| 1548 | − "table": "sources" | |
| 1549 | − }, | |
| 1550 | − { | |
| 1551 | − "type": "text", | |
| 1552 | − "typeSchema": null, | |
| 1553 | − "notNull": false, | |
| 1554 | − "dimensions": 0, | |
| 1555 | − "default": null, | |
| 1556 | − "generated": null, | |
| 1557 | − "identity": null, | |
| 1558 | − "name": "auth_header_name", | |
| 1559 | − "entityType": "columns", | |
| 1560 | − "schema": "public", | |
| 1561 | − "table": "sources" | |
| 1562 | − }, | |
| 1563 | − { | |
| 1564 | − "type": "text", | |
| 1565 | − "typeSchema": null, | |
| 1566 | − "notNull": false, | |
| 1567 | − "dimensions": 0, | |
| 1568 | − "default": null, | |
| 1569 | − "generated": null, | |
| 1570 | − "identity": null, | |
| 1571 | − "name": "auth_prefix", | |
| 1572 | − "entityType": "columns", | |
| 1573 | − "schema": "public", | |
| 1574 | − "table": "sources" | |
| 1575 | − }, | |
| 1576 | − { | |
| 1577 | − "type": "text", | |
| 1578 | − "typeSchema": null, | |
| 1579 | − "notNull": false, | |
| 1580 | − "dimensions": 0, | |
| 1581 | − "default": null, | |
| 1582 | − "generated": null, | |
| 1583 | − "identity": null, | |
| 1584 | − "name": "source_hash", | |
| 1585 | − "entityType": "columns", | |
| 1586 | − "schema": "public", | |
| 1587 | − "table": "sources" | |
| 1588 | − }, | |
| 1589 | − { | |
| 1590 | − "type": "text", | |
| 1591 | − "typeSchema": null, | |
| 1592 | − "notNull": false, | |
| 1593 | − "dimensions": 0, | |
| 1594 | − "default": null, | |
| 1595 | − "generated": null, | |
| 1596 | − "identity": null, | |
| 1597 | − "name": "last_error", | |
| 1598 | − "entityType": "columns", | |
| 1599 | − "schema": "public", | |
| 1600 | − "table": "sources" | |
| 1601 | − }, | |
| 1602 | − { | |
| 1603 | − "type": "bigint", | |
| 1604 | − "typeSchema": null, | |
| 1605 | − "notNull": true, | |
| 1606 | − "dimensions": 0, | |
| 1607 | − "default": null, | |
| 1608 | − "generated": null, | |
| 1609 | − "identity": null, | |
| 1610 | − "name": "created_at", | |
| 1611 | − "entityType": "columns", | |
| 1612 | − "schema": "public", | |
| 1613 | − "table": "sources" | |
| 1614 | − }, | |
| 1615 | − { | |
| 1616 | − "type": "bigint", | |
| 1617 | − "typeSchema": null, | |
| 1618 | − "notNull": true, | |
| 1619 | − "dimensions": 0, | |
| 1620 | − "default": null, | |
| 1621 | − "generated": null, | |
| 1622 | − "identity": null, | |
| 1623 | − "name": "updated_at", | |
| 1624 | − "entityType": "columns", | |
| 1625 | − "schema": "public", | |
| 1626 | − "table": "sources" | |
| 1627 | − }, | |
| 1628 | − { | |
| 1629 | − "type": "text", | |
| 1630 | − "typeSchema": null, | |
| 1631 | − "notNull": true, | |
| 1632 | − "dimensions": 0, | |
| 1633 | − "default": null, | |
| 1634 | − "generated": null, | |
| 1635 | − "identity": null, | |
| 1636 | − "name": "id", | |
| 1637 | − "entityType": "columns", | |
| 1638 | − "schema": "public", | |
| 1639 | − "table": "workspaces" | |
| 1640 | − }, | |
| 1641 | − { | |
| 1642 | − "type": "text", | |
| 1643 | − "typeSchema": null, | |
| 1644 | − "notNull": true, | |
| 1645 | − "dimensions": 0, | |
| 1646 | − "default": null, | |
| 1647 | − "generated": null, | |
| 1648 | − "identity": null, | |
| 1649 | − "name": "organization_id", | |
| 1650 | − "entityType": "columns", | |
| 1651 | − "schema": "public", | |
| 1652 | − "table": "workspaces" | |
| 1653 | − }, | |
| 1654 | − { | |
| 1655 | − "type": "text", | |
| 1656 | − "typeSchema": null, | |
| 1657 | − "notNull": true, | |
| 1658 | − "dimensions": 0, | |
| 1659 | − "default": null, | |
| 1660 | − "generated": null, | |
| 1661 | − "identity": null, | |
| 1662 | − "name": "name", | |
| 1663 | − "entityType": "columns", | |
| 1664 | − "schema": "public", | |
| 1665 | − "table": "workspaces" | |
| 1666 | − }, | |
| 1667 | − { | |
| 1668 | − "type": "text", | |
| 1669 | − "typeSchema": null, | |
| 1670 | − "notNull": false, | |
| 1671 | − "dimensions": 0, | |
| 1672 | − "default": null, | |
| 1673 | − "generated": null, | |
| 1674 | − "identity": null, | |
| 1675 | − "name": "created_by_account_id", | |
| 1676 | − "entityType": "columns", | |
| 1677 | − "schema": "public", | |
| 1678 | − "table": "workspaces" | |
| 1679 | − }, | |
| 1680 | − { | |
| 1681 | − "type": "bigint", | |
| 1682 | − "typeSchema": null, | |
| 1683 | − "notNull": true, | |
| 1684 | − "dimensions": 0, | |
| 1685 | − "default": null, | |
| 1686 | − "generated": null, | |
| 1687 | − "identity": null, | |
| 1688 | − "name": "created_at", | |
| 1689 | − "entityType": "columns", | |
| 1690 | − "schema": "public", | |
| 1691 | − "table": "workspaces" | |
| 1692 | − }, | |
| 1693 | − { | |
| 1694 | − "type": "bigint", | |
| 1695 | − "typeSchema": null, | |
| 1696 | − "notNull": true, | |
| 1697 | − "dimensions": 0, | |
| 1698 | − "default": null, | |
| 1699 | − "generated": null, | |
| 1700 | − "identity": null, | |
| 1701 | − "name": "updated_at", | |
| 1702 | − "entityType": "columns", | |
| 1703 | − "schema": "public", | |
| 1704 | − "table": "workspaces" | |
| 1705 | − }, | |
| 1706 | − { | |
| 1707 | − "nameExplicit": true, | |
| 1708 | − "columns": [ | |
| 1709 | − { | |
| 1710 | − "value": "provider", | |
| 1711 | − "isExpression": false, | |
| 1712 | − "asc": true, | |
| 1713 | − "nullsFirst": false, | |
| 1714 | − "opclass": null | |
| 1715 | − }, | |
| 1716 | − { | |
| 1717 | − "value": "subject", | |
| 1718 | − "isExpression": false, | |
| 1719 | − "asc": true, | |
| 1720 | − "nullsFirst": false, | |
| 1721 | − "opclass": null | |
| 1722 | − } | |
| 1723 | − ], | |
| 1724 | − "isUnique": true, | |
| 1725 | − "where": null, | |
| 1726 | − "with": "", | |
| 1727 | − "method": "btree", | |
| 1728 | − "concurrently": false, | |
| 1729 | − "name": "accounts_provider_subject_idx", | |
| 1730 | − "entityType": "indexes", | |
| 1731 | − "schema": "public", | |
| 1732 | − "table": "accounts" | |
| 1733 | − }, | |
| 1734 | − { | |
| 1735 | − "nameExplicit": true, | |
| 1736 | − "columns": [ | |
| 1737 | − { | |
| 1738 | − "value": "updated_at", | |
| 1739 | − "isExpression": false, | |
| 1740 | − "asc": true, | |
| 1741 | − "nullsFirst": false, | |
| 1742 | − "opclass": null | |
| 1743 | − }, | |
| 1744 | − { | |
| 1745 | − "value": "id", | |
| 1746 | − "isExpression": false, | |
| 1747 | − "asc": true, | |
| 1748 | − "nullsFirst": false, | |
| 1749 | − "opclass": null | |
| 1750 | − } | |
| 1751 | − ], | |
| 1752 | − "isUnique": false, | |
| 1753 | − "where": null, | |
| 1754 | − "with": "", | |
| 1755 | − "method": "btree", | |
| 1756 | − "concurrently": false, | |
| 1757 | − "name": "accounts_updated_idx", | |
| 1758 | − "entityType": "indexes", | |
| 1759 | − "schema": "public", | |
| 1760 | − "table": "accounts" | |
| 1761 | − }, | |
| 1762 | − { | |
| 1763 | − "nameExplicit": true, | |
| 1764 | − "columns": [ | |
| 1765 | − { | |
| 1766 | − "value": "execution_id", | |
| 1767 | − "isExpression": false, | |
| 1768 | − "asc": true, | |
| 1769 | − "nullsFirst": false, | |
| 1770 | − "opclass": null | |
| 1771 | − }, | |
| 1772 | − { | |
| 1773 | − "value": "updated_at", | |
| 1774 | − "isExpression": false, | |
| 1775 | − "asc": true, | |
| 1776 | − "nullsFirst": false, | |
| 1777 | − "opclass": null | |
| 1778 | − }, | |
| 1779 | − { | |
| 1780 | − "value": "id", | |
| 1781 | − "isExpression": false, | |
| 1782 | − "asc": true, | |
| 1783 | − "nullsFirst": false, | |
| 1784 | − "opclass": null | |
| 1785 | − } | |
| 1786 | − ], | |
| 1787 | − "isUnique": false, | |
| 1788 | − "where": null, | |
| 1789 | − "with": "", | |
| 1790 | − "method": "btree", | |
| 1791 | − "concurrently": false, | |
| 1792 | − "name": "execution_interactions_execution_idx", | |
| 1793 | − "entityType": "indexes", | |
| 1794 | − "schema": "public", | |
| 1795 | − "table": "execution_interactions" | |
| 1796 | − }, | |
| 1797 | − { | |
| 1798 | − "nameExplicit": true, | |
| 1799 | − "columns": [ | |
| 1800 | − { | |
| 1801 | − "value": "workspace_id", | |
| 1802 | − "isExpression": false, | |
| 1803 | − "asc": true, | |
| 1804 | − "nullsFirst": false, | |
| 1805 | − "opclass": null | |
| 1806 | − }, | |
| 1807 | − { | |
| 1808 | − "value": "updated_at", | |
| 1809 | − "isExpression": false, | |
| 1810 | − "asc": true, | |
| 1811 | − "nullsFirst": false, | |
| 1812 | − "opclass": null | |
| 1813 | − }, | |
| 1814 | − { | |
| 1815 | − "value": "id", | |
| 1816 | − "isExpression": false, | |
| 1817 | − "asc": true, | |
| 1818 | − "nullsFirst": false, | |
| 1819 | − "opclass": null | |
| 1820 | − } | |
| 1821 | − ], | |
| 1822 | − "isUnique": false, | |
| 1823 | − "where": null, | |
| 1824 | − "with": "", | |
| 1825 | − "method": "btree", | |
| 1826 | − "concurrently": false, | |
| 1827 | − "name": "executions_workspace_idx", | |
| 1828 | − "entityType": "indexes", | |
| 1829 | − "schema": "public", | |
| 1830 | − "table": "executions" | |
| 1831 | − }, | |
| 1832 | − { | |
| 1833 | − "nameExplicit": true, | |
| 1834 | − "columns": [ | |
| 1835 | − { | |
| 1836 | − "value": "organization_id", | |
| 1837 | − "isExpression": false, | |
| 1838 | − "asc": true, | |
| 1839 | − "nullsFirst": false, | |
| 1840 | − "opclass": null | |
| 1841 | − } | |
| 1842 | − ], | |
| 1843 | − "isUnique": false, | |
| 1844 | − "where": null, | |
| 1845 | − "with": "", | |
| 1846 | − "method": "btree", | |
| 1847 | − "concurrently": false, | |
| 1848 | − "name": "organization_memberships_org_idx", | |
| 1849 | − "entityType": "indexes", | |
| 1850 | − "schema": "public", | |
| 1851 | − "table": "organization_memberships" | |
| 1852 | − }, | |
| 1853 | − { | |
| 1854 | − "nameExplicit": true, | |
| 1855 | − "columns": [ | |
| 1856 | − { | |
| 1857 | − "value": "account_id", | |
| 1858 | − "isExpression": false, | |
| 1859 | − "asc": true, | |
| 1860 | − "nullsFirst": false, | |
| 1861 | − "opclass": null | |
| 1862 | − } | |
| 1863 | − ], | |
| 1864 | − "isUnique": false, | |
| 1865 | − "where": null, | |
| 1866 | − "with": "", | |
| 1867 | − "method": "btree", | |
| 1868 | − "concurrently": false, | |
| 1869 | − "name": "organization_memberships_account_idx", | |
| 1870 | − "entityType": "indexes", | |
| 1871 | − "schema": "public", | |
| 1872 | − "table": "organization_memberships" | |
| 1873 | − }, | |
| 1874 | − { | |
| 1875 | − "nameExplicit": true, | |
| 1876 | − "columns": [ | |
| 1877 | − { | |
| 1878 | − "value": "organization_id", | |
| 1879 | − "isExpression": false, | |
| 1880 | − "asc": true, | |
| 1881 | − "nullsFirst": false, | |
| 1882 | − "opclass": null | |
| 1883 | − }, | |
| 1884 | − { | |
| 1885 | − "value": "account_id", | |
| 1886 | − "isExpression": false, | |
| 1887 | − "asc": true, | |
| 1888 | − "nullsFirst": false, | |
| 1889 | − "opclass": null | |
| 1890 | − } | |
| 1891 | − ], | |
| 1892 | − "isUnique": true, | |
| 1893 | − "where": null, | |
| 1894 | − "with": "", | |
| 1895 | − "method": "btree", | |
| 1896 | − "concurrently": false, | |
| 1897 | − "name": "organization_memberships_org_account_idx", | |
| 1898 | − "entityType": "indexes", | |
| 1899 | − "schema": "public", | |
| 1900 | − "table": "organization_memberships" | |
| 1901 | − }, | |
| 1902 | − { | |
| 1903 | − "nameExplicit": true, | |
| 1904 | − "columns": [ | |
| 1905 | − { | |
| 1906 | − "value": "slug", | |
| 1907 | − "isExpression": false, | |
| 1908 | − "asc": true, | |
| 1909 | − "nullsFirst": false, | |
| 1910 | − "opclass": null | |
| 1911 | − } | |
| 1912 | − ], | |
| 1913 | − "isUnique": true, | |
| 1914 | − "where": null, | |
| 1915 | − "with": "", | |
| 1916 | − "method": "btree", | |
| 1917 | − "concurrently": false, | |
| 1918 | − "name": "organizations_slug_idx", | |
| 1919 | − "entityType": "indexes", | |
| 1920 | − "schema": "public", | |
| 1921 | − "table": "organizations" | |
| 1922 | − }, | |
| 1923 | − { | |
| 1924 | − "nameExplicit": true, | |
| 1925 | − "columns": [ | |
| 1926 | − { | |
| 1927 | − "value": "updated_at", | |
| 1928 | − "isExpression": false, | |
| 1929 | − "asc": true, | |
| 1930 | − "nullsFirst": false, | |
| 1931 | − "opclass": null | |
| 1932 | − }, | |
| 1933 | − { | |
| 1934 | − "value": "id", | |
| 1935 | − "isExpression": false, | |
| 1936 | − "asc": true, | |
| 1937 | − "nullsFirst": false, | |
| 1938 | − "opclass": null | |
| 1939 | − } | |
| 1940 | − ], | |
| 1941 | − "isUnique": false, | |
| 1942 | − "where": null, | |
| 1943 | − "with": "", | |
| 1944 | − "method": "btree", | |
| 1945 | − "concurrently": false, | |
| 1946 | − "name": "organizations_updated_idx", | |
| 1947 | − "entityType": "indexes", | |
| 1948 | − "schema": "public", | |
| 1949 | − "table": "organizations" | |
| 1950 | − }, | |
| 1951 | − { | |
| 1952 | − "nameExplicit": true, | |
| 1953 | − "columns": [ | |
| 1954 | − { | |
| 1955 | − "value": "workspace_id", | |
| 1956 | − "isExpression": false, | |
| 1957 | − "asc": true, | |
| 1958 | − "nullsFirst": false, | |
| 1959 | − "opclass": null | |
| 1960 | − }, | |
| 1961 | − { | |
| 1962 | − "value": "updated_at", | |
| 1963 | − "isExpression": false, | |
| 1964 | − "asc": true, | |
| 1965 | − "nullsFirst": false, | |
| 1966 | − "opclass": null | |
| 1967 | − }, | |
| 1968 | − { | |
| 1969 | − "value": "id", | |
| 1970 | − "isExpression": false, | |
| 1971 | − "asc": true, | |
| 1972 | − "nullsFirst": false, | |
| 1973 | − "opclass": null | |
| 1974 | − } | |
| 1975 | − ], | |
| 1976 | − "isUnique": false, | |
| 1977 | − "where": null, | |
| 1978 | − "with": "", | |
| 1979 | − "method": "btree", | |
| 1980 | − "concurrently": false, | |
| 1981 | − "name": "policies_workspace_idx", | |
| 1982 | − "entityType": "indexes", | |
| 1983 | − "schema": "public", | |
| 1984 | − "table": "policies" | |
| 1985 | − }, | |
| 1986 | − { | |
| 1987 | − "nameExplicit": true, | |
| 1988 | − "columns": [ | |
| 1989 | − { | |
| 1990 | − "value": "updated_at", | |
| 1991 | − "isExpression": false, | |
| 1992 | − "asc": true, | |
| 1993 | − "nullsFirst": false, | |
| 1994 | − "opclass": null | |
| 1995 | − }, | |
| 1996 | − { | |
| 1997 | − "value": "id", | |
| 1998 | − "isExpression": false, | |
| 1999 | − "asc": true, | |
| 2000 | − "nullsFirst": false, | |
| 2001 | − "opclass": null | |
| 2002 | − } | |
| 2003 | − ], | |
| 2004 | − "isUnique": false, | |
| 2005 | − "where": null, | |
| 2006 | − "with": "", | |
| 2007 | − "method": "btree", | |
| 2008 | − "concurrently": false, | |
| 2009 | − "name": "secret_materials_updated_idx", | |
| 2010 | − "entityType": "indexes", | |
| 2011 | − "schema": "public", | |
| 2012 | − "table": "secret_materials" | |
| 2013 | − }, | |
| 2014 | − { | |
| 2015 | − "nameExplicit": true, | |
| 2016 | − "columns": [ | |
| 2017 | − { | |
| 2018 | − "value": "workspace_id", | |
| 2019 | − "isExpression": false, | |
| 2020 | − "asc": true, | |
| 2021 | − "nullsFirst": false, | |
| 2022 | − "opclass": null | |
| 2023 | − }, | |
| 2024 | − { | |
| 2025 | − "value": "updated_at", | |
| 2026 | − "isExpression": false, | |
| 2027 | − "asc": true, | |
| 2028 | − "nullsFirst": false, | |
| 2029 | − "opclass": null | |
| 2030 | − }, | |
| 2031 | − { | |
| 2032 | − "value": "id", | |
| 2033 | − "isExpression": false, | |
| 2034 | − "asc": true, | |
| 2035 | − "nullsFirst": false, | |
| 2036 | − "opclass": null | |
| 2037 | − } | |
| 2038 | − ], | |
| 2039 | − "isUnique": false, | |
| 2040 | − "where": null, | |
| 2041 | − "with": "", | |
| 2042 | − "method": "btree", | |
| 2043 | − "concurrently": false, | |
| 2044 | − "name": "source_auth_sessions_workspace_idx", | |
| 2045 | − "entityType": "indexes", | |
| 2046 | − "schema": "public", | |
| 2047 | − "table": "source_auth_sessions" | |
| 2048 | − }, | |
| 2049 | − { | |
| 2050 | − "nameExplicit": true, | |
| 2051 | − "columns": [ | |
| 2052 | − { | |
| 2053 | − "value": "state", | |
| 2054 | − "isExpression": false, | |
| 2055 | − "asc": true, | |
| 2056 | − "nullsFirst": false, | |
| 2057 | − "opclass": null | |
| 2058 | − } | |
| 2059 | − ], | |
| 2060 | − "isUnique": true, | |
| 2061 | − "where": null, | |
| 2062 | − "with": "", | |
| 2063 | − "method": "btree", | |
| 2064 | − "concurrently": false, | |
| 2065 | − "name": "source_auth_sessions_state_idx", | |
| 2066 | − "entityType": "indexes", | |
| 2067 | − "schema": "public", | |
| 2068 | − "table": "source_auth_sessions" | |
| 2069 | − }, | |
| 2070 | − { | |
| 2071 | − "nameExplicit": true, | |
| 2072 | − "columns": [ | |
| 2073 | − { | |
| 2074 | − "value": "workspace_id", | |
| 2075 | − "isExpression": false, | |
| 2076 | − "asc": true, | |
| 2077 | − "nullsFirst": false, | |
| 2078 | − "opclass": null | |
| 2079 | − }, | |
| 2080 | − { | |
| 2081 | − "value": "updated_at", | |
| 2082 | − "isExpression": false, | |
| 2083 | − "asc": true, | |
| 2084 | − "nullsFirst": false, | |
| 2085 | − "opclass": null | |
| 2086 | − }, | |
| 2087 | − { | |
| 2088 | − "value": "source_id", | |
| 2089 | − "isExpression": false, | |
| 2090 | − "asc": true, | |
| 2091 | − "nullsFirst": false, | |
| 2092 | − "opclass": null | |
| 2093 | − } | |
| 2094 | − ], | |
| 2095 | − "isUnique": false, | |
| 2096 | − "where": null, | |
| 2097 | − "with": "", | |
| 2098 | − "method": "btree", | |
| 2099 | − "concurrently": false, | |
| 2100 | − "name": "source_credential_bindings_workspace_idx", | |
| 2101 | − "entityType": "indexes", | |
| 2102 | − "schema": "public", | |
| 2103 | − "table": "source_credential_bindings" | |
| 2104 | − }, | |
| 2105 | − { | |
| 2106 | − "nameExplicit": true, | |
| 2107 | − "columns": [ | |
| 2108 | − { | |
| 2109 | − "value": "workspace_id", | |
| 2110 | − "isExpression": false, | |
| 2111 | − "asc": true, | |
| 2112 | − "nullsFirst": false, | |
| 2113 | − "opclass": null | |
| 2114 | − }, | |
| 2115 | − { | |
| 2116 | − "value": "name", | |
| 2117 | − "isExpression": false, | |
| 2118 | − "asc": true, | |
| 2119 | − "nullsFirst": false, | |
| 2120 | − "opclass": null | |
| 2121 | − } | |
| 2122 | − ], | |
| 2123 | − "isUnique": true, | |
| 2124 | − "where": null, | |
| 2125 | − "with": "", | |
| 2126 | − "method": "btree", | |
| 2127 | − "concurrently": false, | |
| 2128 | − "name": "sources_workspace_name_idx", | |
| 2129 | − "entityType": "indexes", | |
| 2130 | − "schema": "public", | |
| 2131 | − "table": "sources" | |
| 2132 | − }, | |
| 2133 | − { | |
| 2134 | − "nameExplicit": true, | |
| 2135 | − "columns": [ | |
| 2136 | − { | |
| 2137 | − "value": "organization_id", | |
| 2138 | − "isExpression": false, | |
| 2139 | − "asc": true, | |
| 2140 | − "nullsFirst": false, | |
| 2141 | − "opclass": null | |
| 2142 | − } | |
| 2143 | − ], | |
| 2144 | − "isUnique": false, | |
| 2145 | − "where": null, | |
| 2146 | − "with": "", | |
| 2147 | − "method": "btree", | |
| 2148 | − "concurrently": false, | |
| 2149 | − "name": "workspaces_org_idx", | |
| 2150 | − "entityType": "indexes", | |
| 2151 | − "schema": "public", | |
| 2152 | − "table": "workspaces" | |
| 2153 | − }, | |
| 2154 | − { | |
| 2155 | − "nameExplicit": true, | |
| 2156 | − "columns": [ | |
| 2157 | − { | |
| 2158 | − "value": "organization_id", | |
| 2159 | − "isExpression": false, | |
| 2160 | − "asc": true, | |
| 2161 | − "nullsFirst": false, | |
| 2162 | − "opclass": null | |
| 2163 | − }, | |
| 2164 | − { | |
| 2165 | − "value": "name", | |
| 2166 | − "isExpression": false, | |
| 2167 | − "asc": true, | |
| 2168 | − "nullsFirst": false, | |
| 2169 | − "opclass": null | |
| 2170 | − } | |
| 2171 | − ], | |
| 2172 | − "isUnique": true, | |
| 2173 | − "where": null, | |
| 2174 | − "with": "", | |
| 2175 | − "method": "btree", | |
| 2176 | − "concurrently": false, | |
| 2177 | − "name": "workspaces_org_name_idx", | |
| 2178 | − "entityType": "indexes", | |
| 2179 | − "schema": "public", | |
| 2180 | − "table": "workspaces" | |
| 2181 | − }, | |
| 2182 | − { | |
| 2183 | − "columns": [ | |
| 2184 | − "workspace_id", | |
| 2185 | − "source_id" | |
| 2186 | − ], | |
| 2187 | − "nameExplicit": false, | |
| 2188 | − "name": "source_credential_bindings_pkey", | |
| 2189 | − "entityType": "pks", | |
| 2190 | − "schema": "public", | |
| 2191 | − "table": "source_credential_bindings" | |
| 2192 | − }, | |
| 2193 | − { | |
| 2194 | − "columns": [ | |
| 2195 | − "workspace_id", | |
| 2196 | − "source_id" | |
| 2197 | − ], | |
| 2198 | − "nameExplicit": false, | |
| 2199 | − "name": "sources_pkey", | |
| 2200 | − "entityType": "pks", | |
| 2201 | − "schema": "public", | |
| 2202 | − "table": "sources" | |
| 2203 | − }, | |
| 2204 | − { | |
| 2205 | − "columns": [ | |
| 2206 | − "id" | |
| 2207 | − ], | |
| 2208 | − "nameExplicit": false, | |
| 2209 | − "name": "accounts_pkey", | |
| 2210 | − "schema": "public", | |
| 2211 | − "table": "accounts", | |
| 2212 | − "entityType": "pks" | |
| 2213 | − }, | |
| 2214 | − { | |
| 2215 | − "columns": [ | |
| 2216 | − "id" | |
| 2217 | − ], | |
| 2218 | − "nameExplicit": false, | |
| 2219 | − "name": "execution_interactions_pkey", | |
| 2220 | − "schema": "public", | |
| 2221 | − "table": "execution_interactions", | |
| 2222 | − "entityType": "pks" | |
| 2223 | − }, | |
| 2224 | − { | |
| 2225 | − "columns": [ | |
| 2226 | − "id" | |
| 2227 | − ], | |
| 2228 | − "nameExplicit": false, | |
| 2229 | − "name": "executions_pkey", | |
| 2230 | − "schema": "public", | |
| 2231 | − "table": "executions", | |
| 2232 | − "entityType": "pks" | |
| 2233 | − }, | |
| 2234 | − { | |
| 2235 | − "columns": [ | |
| 2236 | − "id" | |
| 2237 | − ], | |
| 2238 | − "nameExplicit": false, | |
| 2239 | − "name": "local_installations_pkey", | |
| 2240 | − "schema": "public", | |
| 2241 | − "table": "local_installations", | |
| 2242 | − "entityType": "pks" | |
| 2243 | − }, | |
| 2244 | − { | |
| 2245 | − "columns": [ | |
| 2246 | − "id" | |
| 2247 | − ], | |
| 2248 | − "nameExplicit": false, | |
| 2249 | − "name": "organization_memberships_pkey", | |
| 2250 | − "schema": "public", | |
| 2251 | − "table": "organization_memberships", | |
| 2252 | − "entityType": "pks" | |
| 2253 | − }, | |
| 2254 | − { | |
| 2255 | − "columns": [ | |
| 2256 | − "id" | |
| 2257 | − ], | |
| 2258 | − "nameExplicit": false, | |
| 2259 | − "name": "organizations_pkey", | |
| 2260 | − "schema": "public", | |
| 2261 | − "table": "organizations", | |
| 2262 | − "entityType": "pks" | |
| 2263 | − }, | |
| 2264 | − { | |
| 2265 | − "columns": [ | |
| 2266 | − "id" | |
| 2267 | − ], | |
| 2268 | − "nameExplicit": false, | |
| 2269 | − "name": "policies_pkey", | |
| 2270 | − "schema": "public", | |
| 2271 | − "table": "policies", | |
| 2272 | − "entityType": "pks" | |
| 2273 | − }, | |
| 2274 | − { | |
| 2275 | − "columns": [ | |
| 2276 | − "id" | |
| 2277 | − ], | |
| 2278 | − "nameExplicit": false, | |
| 2279 | − "name": "secret_materials_pkey", | |
| 2280 | − "schema": "public", | |
| 2281 | − "table": "secret_materials", | |
| 2282 | − "entityType": "pks" | |
| 2283 | − }, | |
| 2284 | − { | |
| 2285 | − "columns": [ | |
| 2286 | − "id" | |
| 2287 | − ], | |
| 2288 | − "nameExplicit": false, | |
| 2289 | − "name": "source_auth_sessions_pkey", | |
| 2290 | − "schema": "public", | |
| 2291 | − "table": "source_auth_sessions", | |
| 2292 | − "entityType": "pks" | |
| 2293 | − }, | |
| 2294 | − { | |
| 2295 | − "columns": [ | |
| 2296 | − "id" | |
| 2297 | − ], | |
| 2298 | − "nameExplicit": false, | |
| 2299 | − "name": "workspaces_pkey", | |
| 2300 | − "schema": "public", | |
| 2301 | − "table": "workspaces", | |
| 2302 | − "entityType": "pks" | |
| 2303 | − }, | |
| 2304 | − { | |
| 2305 | − "value": "\"provider\" in ('local', 'workos', 'service')", | |
| 2306 | − "name": "accounts_provider_check", | |
| 2307 | − "entityType": "checks", | |
| 2308 | − "schema": "public", | |
| 2309 | − "table": "accounts" | |
| 2310 | − }, | |
| 2311 | − { | |
| 2312 | − "value": "\"status\" in ('pending', 'resolved', 'cancelled')", | |
| 2313 | − "name": "execution_interactions_status_check", | |
| 2314 | − "entityType": "checks", | |
| 2315 | − "schema": "public", | |
| 2316 | − "table": "execution_interactions" | |
| 2317 | − }, | |
| 2318 | − { | |
| 2319 | − "value": "\"status\" in ('pending', 'running', 'waiting_for_interaction', 'completed', 'failed', 'cancelled')", | |
| 2320 | − "name": "executions_status_check", | |
| 2321 | − "entityType": "checks", | |
| 2322 | − "schema": "public", | |
| 2323 | − "table": "executions" | |
| 2324 | − }, | |
| 2325 | − { | |
| 2326 | − "value": "\"role\" in ('viewer', 'editor', 'admin', 'owner')", | |
| 2327 | − "name": "organization_memberships_role_check", | |
| 2328 | − "entityType": "checks", | |
| 2329 | − "schema": "public", | |
| 2330 | − "table": "organization_memberships" | |
| 2331 | − }, | |
| 2332 | − { | |
| 2333 | − "value": "\"status\" in ('invited', 'active', 'suspended', 'removed')", | |
| 2334 | − "name": "organization_memberships_status_check", | |
| 2335 | − "entityType": "checks", | |
| 2336 | − "schema": "public", | |
| 2337 | − "table": "organization_memberships" | |
| 2338 | − }, | |
| 2339 | − { | |
| 2340 | − "value": "\"status\" in ('active', 'suspended', 'archived')", | |
| 2341 | − "name": "organizations_status_check", | |
| 2342 | − "entityType": "checks", | |
| 2343 | − "schema": "public", | |
| 2344 | − "table": "organizations" | |
| 2345 | − }, | |
| 2346 | − { | |
| 2347 | − "value": "\"resource_type\" in ('all_tools', 'source', 'namespace', 'tool_path')", | |
| 2348 | − "name": "policies_resource_type_check", | |
| 2349 | − "entityType": "checks", | |
| 2350 | − "schema": "public", | |
| 2351 | − "table": "policies" | |
| 2352 | − }, | |
| 2353 | − { | |
| 2354 | − "value": "\"match_type\" in ('glob', 'exact')", | |
| 2355 | − "name": "policies_match_type_check", | |
| 2356 | − "entityType": "checks", | |
| 2357 | − "schema": "public", | |
| 2358 | − "table": "policies" | |
| 2359 | − }, | |
| 2360 | − { | |
| 2361 | − "value": "\"effect\" in ('allow', 'deny')", | |
| 2362 | − "name": "policies_effect_check", | |
| 2363 | − "entityType": "checks", | |
| 2364 | − "schema": "public", | |
| 2365 | − "table": "policies" | |
| 2366 | − }, | |
| 2367 | − { | |
| 2368 | − "value": "\"approval_mode\" in ('auto', 'required')", | |
| 2369 | − "name": "policies_approval_mode_check", | |
| 2370 | − "entityType": "checks", | |
| 2371 | − "schema": "public", | |
| 2372 | − "table": "policies" | |
| 2373 | − }, | |
| 2374 | − { | |
| 2375 | − "value": "\"purpose\" in ('auth_material', 'oauth_access_token', 'oauth_refresh_token', 'oauth_client_info')", | |
| 2376 | − "name": "secret_materials_purpose_check", | |
| 2377 | − "entityType": "checks", | |
| 2378 | − "schema": "public", | |
| 2379 | − "table": "secret_materials" | |
| 2380 | − }, | |
| 2381 | − { | |
| 2382 | − "value": "\"strategy\" in ('oauth2_authorization_code')", | |
| 2383 | − "name": "source_auth_sessions_strategy_check", | |
| 2384 | − "entityType": "checks", | |
| 2385 | − "schema": "public", | |
| 2386 | − "table": "source_auth_sessions" | |
| 2387 | − }, | |
| 2388 | − { | |
| 2389 | − "value": "\"status\" in ('pending', 'completed', 'failed', 'cancelled')", | |
| 2390 | − "name": "source_auth_sessions_status_check", | |
| 2391 | − "entityType": "checks", | |
| 2392 | − "schema": "public", | |
| 2393 | − "table": "source_auth_sessions" | |
| 2394 | − }, | |
| 2395 | − { | |
| 2396 | − "value": "\"kind\" in ('mcp', 'openapi', 'graphql', 'internal')", | |
| 2397 | − "name": "sources_kind_check", | |
| 2398 | − "entityType": "checks", | |
| 2399 | − "schema": "public", | |
| 2400 | − "table": "sources" | |
| 2401 | − }, | |
| 2402 | − { | |
| 2403 | − "value": "\"status\" in ('draft', 'probing', 'auth_required', 'connected', 'error')", | |
| 2404 | − "name": "sources_status_check", | |
| 2405 | − "entityType": "checks", | |
| 2406 | − "schema": "public", | |
| 2407 | − "table": "sources" | |
| 2408 | − }, | |
| 2409 | − { | |
| 2410 | − "value": "\"transport\" is null or \"transport\" in ('auto', 'streamable-http', 'sse')", | |
| 2411 | − "name": "sources_transport_check", | |
| 2412 | − "entityType": "checks", | |
| 2413 | − "schema": "public", | |
| 2414 | − "table": "sources" | |
| 2415 | − }, | |
| 2416 | − { | |
| 2417 | − "value": "\"auth_kind\" in ('none', 'bearer', 'oauth2')", | |
| 2418 | − "name": "sources_auth_kind_check", | |
| 2419 | − "entityType": "checks", | |
| 2420 | − "schema": "public", | |
| 2421 | − "table": "sources" | |
| 2422 | − } | |
| 2423 | − ], | |
| 2424 | − "renames": [] | |
| 2425 | −} | |
| @@ -1,70 +0,0 @@ | ||
| 1 | −CREATE TABLE "tool_artifact_parameters" ( | |
| 2 | − "workspace_id" text, | |
| 3 | − "path" text, | |
| 4 | − "position" bigint, | |
| 5 | − "name" text NOT NULL, | |
| 6 | − "location" text NOT NULL, | |
| 7 | − "required" boolean NOT NULL, | |
| 8 | − CONSTRAINT "tool_artifact_parameters_pkey" PRIMARY KEY("workspace_id","path","position"), | |
| 9 | − CONSTRAINT "tool_artifact_parameters_location_check" CHECK ("location" in ('path', 'query', 'header', 'cookie')) | |
| 10 | −); | |
| 11 | −--> statement-breakpoint | |
| 12 | −CREATE TABLE "tool_artifact_ref_hint_keys" ( | |
| 13 | − "workspace_id" text, | |
| 14 | − "path" text, | |
| 15 | − "position" bigint, | |
| 16 | − "ref_hint_key" text NOT NULL, | |
| 17 | − CONSTRAINT "tool_artifact_ref_hint_keys_pkey" PRIMARY KEY("workspace_id","path","position") | |
| 18 | −); | |
| 19 | −--> statement-breakpoint | |
| 20 | −CREATE TABLE "tool_artifact_request_body_content_types" ( | |
| 21 | − "workspace_id" text, | |
| 22 | − "path" text, | |
| 23 | − "position" bigint, | |
| 24 | − "content_type" text NOT NULL, | |
| 25 | − CONSTRAINT "tool_artifact_request_body_content_types_pkey" PRIMARY KEY("workspace_id","path","position") | |
| 26 | −); | |
| 27 | −--> statement-breakpoint | |
| 28 | −CREATE TABLE "tool_artifacts" ( | |
| 29 | − "workspace_id" text, | |
| 30 | − "path" text, | |
| 31 | − "tool_id" text NOT NULL, | |
| 32 | − "source_id" text NOT NULL, | |
| 33 | − "title" text, | |
| 34 | − "description" text, | |
| 35 | − "search_namespace" text NOT NULL, | |
| 36 | − "search_text" text NOT NULL, | |
| 37 | − "input_schema_json" text, | |
| 38 | − "output_schema_json" text, | |
| 39 | − "provider_kind" text NOT NULL, | |
| 40 | − "mcp_tool_name" text, | |
| 41 | − "openapi_method" text, | |
| 42 | − "openapi_path_template" text, | |
| 43 | − "openapi_operation_hash" text, | |
| 44 | − "openapi_request_body_required" boolean, | |
| 45 | − "created_at" bigint NOT NULL, | |
| 46 | − "updated_at" bigint NOT NULL, | |
| 47 | − CONSTRAINT "tool_artifacts_pkey" PRIMARY KEY("workspace_id","path"), | |
| 48 | − CONSTRAINT "tool_artifacts_provider_kind_check" CHECK ("provider_kind" in ('mcp', 'openapi')), | |
| 49 | − CONSTRAINT "tool_artifacts_mcp_shape_check" CHECK ("provider_kind" <> 'mcp' | |
| 50 | − or ( | |
| 51 | − "mcp_tool_name" is not null | |
| 52 | − and "openapi_method" is null | |
| 53 | − and "openapi_path_template" is null | |
| 54 | − and "openapi_operation_hash" is null | |
| 55 | − and "openapi_request_body_required" is null | |
| 56 | − )), | |
| 57 | − CONSTRAINT "tool_artifacts_openapi_shape_check" CHECK ("provider_kind" <> 'openapi' | |
| 58 | − or ( | |
| 59 | − "mcp_tool_name" is null | |
| 60 | − and "openapi_method" in ('get', 'put', 'post', 'delete', 'patch', 'head', 'options', 'trace') | |
| 61 | − and "openapi_path_template" is not null | |
| 62 | − and "openapi_operation_hash" is not null | |
| 63 | − )) | |
| 64 | −); | |
| 65 | −--> statement-breakpoint | |
| 66 | −CREATE INDEX "tool_artifact_parameters_lookup_idx" ON "tool_artifact_parameters" ("workspace_id","path","position");--> statement-breakpoint | |
| 67 | −CREATE INDEX "tool_artifact_ref_hint_keys_lookup_idx" ON "tool_artifact_ref_hint_keys" ("workspace_id","path","position");--> statement-breakpoint | |
| 68 | −CREATE INDEX "tool_artifact_request_body_content_types_lookup_idx" ON "tool_artifact_request_body_content_types" ("workspace_id","path","position");--> statement-breakpoint | |
| 69 | −CREATE INDEX "tool_artifacts_workspace_source_idx" ON "tool_artifacts" ("workspace_id","source_id","updated_at","path");--> statement-breakpoint | |
| 70 | −CREATE INDEX "tool_artifacts_workspace_namespace_idx" ON "tool_artifacts" ("workspace_id","search_namespace","path"); | |
| @@ -1,3122 +0,0 @@ | ||
| 1 | −{ | |
| 2 | − "version": "8", | |
| 3 | − "dialect": "postgres", | |
| 4 | − "id": "3e9aa0ec-194c-4364-9e57-4539db8fb2ad", | |
| 5 | − "prevIds": [ | |
| 6 | − "1141497b-c500-44ff-87db-6a440062becb" | |
| 7 | − ], | |
| 8 | − "ddl": [ | |
| 9 | − { | |
| 10 | − "isRlsEnabled": false, | |
| 11 | − "name": "accounts", | |
| 12 | − "entityType": "tables", | |
| 13 | − "schema": "public" | |
| 14 | − }, | |
| 15 | − { | |
| 16 | − "isRlsEnabled": false, | |
| 17 | − "name": "execution_interactions", | |
| 18 | − "entityType": "tables", | |
| 19 | − "schema": "public" | |
| 20 | − }, | |
| 21 | − { | |
| 22 | − "isRlsEnabled": false, | |
| 23 | − "name": "executions", | |
| 24 | − "entityType": "tables", | |
| 25 | − "schema": "public" | |
| 26 | − }, | |
| 27 | − { | |
| 28 | − "isRlsEnabled": false, | |
| 29 | − "name": "local_installations", | |
| 30 | − "entityType": "tables", | |
| 31 | − "schema": "public" | |
| 32 | − }, | |
| 33 | − { | |
| 34 | − "isRlsEnabled": false, | |
| 35 | − "name": "organization_memberships", | |
| 36 | − "entityType": "tables", | |
| 37 | − "schema": "public" | |
| 38 | − }, | |
| 39 | − { | |
| 40 | − "isRlsEnabled": false, | |
| 41 | − "name": "organizations", | |
| 42 | − "entityType": "tables", | |
| 43 | − "schema": "public" | |
| 44 | − }, | |
| 45 | − { | |
| 46 | − "isRlsEnabled": false, | |
| 47 | − "name": "policies", | |
| 48 | − "entityType": "tables", | |
| 49 | − "schema": "public" | |
| 50 | − }, | |
| 51 | − { | |
| 52 | − "isRlsEnabled": false, | |
| 53 | − "name": "secret_materials", | |
| 54 | − "entityType": "tables", | |
| 55 | − "schema": "public" | |
| 56 | − }, | |
| 57 | − { | |
| 58 | − "isRlsEnabled": false, | |
| 59 | − "name": "source_auth_sessions", | |
| 60 | − "entityType": "tables", | |
| 61 | − "schema": "public" | |
| 62 | − }, | |
| 63 | − { | |
| 64 | − "isRlsEnabled": false, | |
| 65 | − "name": "source_credential_bindings", | |
| 66 | − "entityType": "tables", | |
| 67 | − "schema": "public" | |
| 68 | − }, | |
| 69 | − { | |
| 70 | − "isRlsEnabled": false, | |
| 71 | − "name": "sources", | |
| 72 | − "entityType": "tables", | |
| 73 | − "schema": "public" | |
| 74 | − }, | |
| 75 | − { | |
| 76 | − "isRlsEnabled": false, | |
| 77 | − "name": "tool_artifact_parameters", | |
| 78 | − "entityType": "tables", | |
| 79 | − "schema": "public" | |
| 80 | − }, | |
| 81 | − { | |
| 82 | − "isRlsEnabled": false, | |
| 83 | − "name": "tool_artifact_ref_hint_keys", | |
| 84 | − "entityType": "tables", | |
| 85 | − "schema": "public" | |
| 86 | − }, | |
| 87 | − { | |
| 88 | − "isRlsEnabled": false, | |
| 89 | − "name": "tool_artifact_request_body_content_types", | |
| 90 | − "entityType": "tables", | |
| 91 | − "schema": "public" | |
| 92 | − }, | |
| 93 | − { | |
| 94 | − "isRlsEnabled": false, | |
| 95 | − "name": "tool_artifacts", | |
| 96 | − "entityType": "tables", | |
| 97 | − "schema": "public" | |
| 98 | − }, | |
| 99 | − { | |
| 100 | − "isRlsEnabled": false, | |
| 101 | − "name": "workspaces", | |
| 102 | − "entityType": "tables", | |
| 103 | − "schema": "public" | |
| 104 | − }, | |
| 105 | − { | |
| 106 | − "type": "text", | |
| 107 | − "typeSchema": null, | |
| 108 | − "notNull": true, | |
| 109 | − "dimensions": 0, | |
| 110 | − "default": null, | |
| 111 | − "generated": null, | |
| 112 | − "identity": null, | |
| 113 | − "name": "id", | |
| 114 | − "entityType": "columns", | |
| 115 | − "schema": "public", | |
| 116 | − "table": "accounts" | |
| 117 | − }, | |
| 118 | − { | |
| 119 | − "type": "text", | |
| 120 | − "typeSchema": null, | |
| 121 | − "notNull": true, | |
| 122 | − "dimensions": 0, | |
| 123 | − "default": null, | |
| 124 | − "generated": null, | |
| 125 | − "identity": null, | |
| 126 | − "name": "provider", | |
| 127 | − "entityType": "columns", | |
| 128 | − "schema": "public", | |
| 129 | − "table": "accounts" | |
| 130 | − }, | |
| 131 | − { | |
| 132 | − "type": "text", | |
| 133 | − "typeSchema": null, | |
| 134 | − "notNull": true, | |
| 135 | − "dimensions": 0, | |
| 136 | − "default": null, | |
| 137 | − "generated": null, | |
| 138 | − "identity": null, | |
| 139 | − "name": "subject", | |
| 140 | − "entityType": "columns", | |
| 141 | − "schema": "public", | |
| 142 | − "table": "accounts" | |
| 143 | − }, | |
| 144 | − { | |
| 145 | − "type": "text", | |
| 146 | − "typeSchema": null, | |
| 147 | − "notNull": false, | |
| 148 | − "dimensions": 0, | |
| 149 | − "default": null, | |
| 150 | − "generated": null, | |
| 151 | − "identity": null, | |
| 152 | − "name": "email", | |
| 153 | − "entityType": "columns", | |
| 154 | − "schema": "public", | |
| 155 | − "table": "accounts" | |
| 156 | − }, | |
| 157 | − { | |
| 158 | − "type": "text", | |
| 159 | − "typeSchema": null, | |
| 160 | − "notNull": false, | |
| 161 | − "dimensions": 0, | |
| 162 | − "default": null, | |
| 163 | − "generated": null, | |
| 164 | − "identity": null, | |
| 165 | − "name": "display_name", | |
| 166 | − "entityType": "columns", | |
| 167 | − "schema": "public", | |
| 168 | − "table": "accounts" | |
| 169 | − }, | |
| 170 | − { | |
| 171 | − "type": "bigint", | |
| 172 | − "typeSchema": null, | |
| 173 | − "notNull": true, | |
| 174 | − "dimensions": 0, | |
| 175 | − "default": null, | |
| 176 | − "generated": null, | |
| 177 | − "identity": null, | |
| 178 | − "name": "created_at", | |
| 179 | − "entityType": "columns", | |
| 180 | − "schema": "public", | |
| 181 | − "table": "accounts" | |
| 182 | − }, | |
| 183 | − { | |
| 184 | − "type": "bigint", | |
| 185 | − "typeSchema": null, | |
| 186 | − "notNull": true, | |
| 187 | − "dimensions": 0, | |
| 188 | − "default": null, | |
| 189 | − "generated": null, | |
| 190 | − "identity": null, | |
| 191 | − "name": "updated_at", | |
| 192 | − "entityType": "columns", | |
| 193 | − "schema": "public", | |
| 194 | − "table": "accounts" | |
| 195 | − }, | |
| 196 | − { | |
| 197 | − "type": "text", | |
| 198 | − "typeSchema": null, | |
| 199 | − "notNull": true, | |
| 200 | − "dimensions": 0, | |
| 201 | − "default": null, | |
| 202 | − "generated": null, | |
| 203 | − "identity": null, | |
| 204 | − "name": "id", | |
| 205 | − "entityType": "columns", | |
| 206 | − "schema": "public", | |
| 207 | − "table": "execution_interactions" | |
| 208 | − }, | |
| 209 | − { | |
| 210 | − "type": "text", | |
| 211 | − "typeSchema": null, | |
| 212 | − "notNull": true, | |
| 213 | − "dimensions": 0, | |
| 214 | − "default": null, | |
| 215 | − "generated": null, | |
| 216 | − "identity": null, | |
| 217 | − "name": "execution_id", | |
| 218 | − "entityType": "columns", | |
| 219 | − "schema": "public", | |
| 220 | − "table": "execution_interactions" | |
| 221 | − }, | |
| 222 | − { | |
| 223 | − "type": "text", | |
| 224 | − "typeSchema": null, | |
| 225 | − "notNull": true, | |
| 226 | − "dimensions": 0, | |
| 227 | − "default": null, | |
| 228 | − "generated": null, | |
| 229 | − "identity": null, | |
| 230 | − "name": "status", | |
| 231 | − "entityType": "columns", | |
| 232 | − "schema": "public", | |
| 233 | − "table": "execution_interactions" | |
| 234 | − }, | |
| 235 | − { | |
| 236 | − "type": "text", | |
| 237 | − "typeSchema": null, | |
| 238 | − "notNull": true, | |
| 239 | − "dimensions": 0, | |
| 240 | − "default": null, | |
| 241 | − "generated": null, | |
| 242 | − "identity": null, | |
| 243 | − "name": "kind", | |
| 244 | − "entityType": "columns", | |
| 245 | − "schema": "public", | |
| 246 | − "table": "execution_interactions" | |
| 247 | − }, | |
| 248 | − { | |
| 249 | − "type": "text", | |
| 250 | − "typeSchema": null, | |
| 251 | − "notNull": true, | |
| 252 | − "dimensions": 0, | |
| 253 | − "default": null, | |
| 254 | − "generated": null, | |
| 255 | − "identity": null, | |
| 256 | − "name": "payload_json", | |
| 257 | − "entityType": "columns", | |
| 258 | − "schema": "public", | |
| 259 | − "table": "execution_interactions" | |
| 260 | − }, | |
| 261 | − { | |
| 262 | − "type": "text", | |
| 263 | − "typeSchema": null, | |
| 264 | − "notNull": false, | |
| 265 | − "dimensions": 0, | |
| 266 | − "default": null, | |
| 267 | − "generated": null, | |
| 268 | − "identity": null, | |
| 269 | − "name": "response_json", | |
| 270 | − "entityType": "columns", | |
| 271 | − "schema": "public", | |
| 272 | − "table": "execution_interactions" | |
| 273 | − }, | |
| 274 | − { | |
| 275 | − "type": "bigint", | |
| 276 | − "typeSchema": null, | |
| 277 | − "notNull": true, | |
| 278 | − "dimensions": 0, | |
| 279 | − "default": null, | |
| 280 | − "generated": null, | |
| 281 | − "identity": null, | |
| 282 | − "name": "created_at", | |
| 283 | − "entityType": "columns", | |
| 284 | − "schema": "public", | |
| 285 | − "table": "execution_interactions" | |
| 286 | − }, | |
| 287 | − { | |
| 288 | − "type": "bigint", | |
| 289 | − "typeSchema": null, | |
| 290 | − "notNull": true, | |
| 291 | − "dimensions": 0, | |
| 292 | − "default": null, | |
| 293 | − "generated": null, | |
| 294 | − "identity": null, | |
| 295 | − "name": "updated_at", | |
| 296 | − "entityType": "columns", | |
| 297 | − "schema": "public", | |
| 298 | − "table": "execution_interactions" | |
| 299 | − }, | |
| 300 | − { | |
| 301 | − "type": "text", | |
| 302 | − "typeSchema": null, | |
| 303 | − "notNull": true, | |
| 304 | − "dimensions": 0, | |
| 305 | − "default": null, | |
| 306 | − "generated": null, | |
| 307 | − "identity": null, | |
| 308 | − "name": "id", | |
| 309 | − "entityType": "columns", | |
| 310 | − "schema": "public", | |
| 311 | − "table": "executions" | |
| 312 | − }, | |
| 313 | − { | |
| 314 | − "type": "text", | |
| 315 | − "typeSchema": null, | |
| 316 | − "notNull": true, | |
| 317 | − "dimensions": 0, | |
| 318 | − "default": null, | |
| 319 | − "generated": null, | |
| 320 | − "identity": null, | |
| 321 | − "name": "workspace_id", | |
| 322 | − "entityType": "columns", | |
| 323 | − "schema": "public", | |
| 324 | − "table": "executions" | |
| 325 | − }, | |
| 326 | − { | |
| 327 | − "type": "text", | |
| 328 | − "typeSchema": null, | |
| 329 | − "notNull": true, | |
| 330 | − "dimensions": 0, | |
| 331 | − "default": null, | |
| 332 | − "generated": null, | |
| 333 | − "identity": null, | |
| 334 | − "name": "created_by_account_id", | |
| 335 | − "entityType": "columns", | |
| 336 | − "schema": "public", | |
| 337 | − "table": "executions" | |
| 338 | − }, | |
| 339 | − { | |
| 340 | − "type": "text", | |
| 341 | − "typeSchema": null, | |
| 342 | − "notNull": true, | |
| 343 | − "dimensions": 0, | |
| 344 | − "default": null, | |
| 345 | − "generated": null, | |
| 346 | − "identity": null, | |
| 347 | − "name": "status", | |
| 348 | − "entityType": "columns", | |
| 349 | − "schema": "public", | |
| 350 | − "table": "executions" | |
| 351 | − }, | |
| 352 | − { | |
| 353 | − "type": "text", | |
| 354 | − "typeSchema": null, | |
| 355 | − "notNull": true, | |
| 356 | − "dimensions": 0, | |
| 357 | − "default": null, | |
| 358 | − "generated": null, | |
| 359 | − "identity": null, | |
| 360 | − "name": "code", | |
| 361 | − "entityType": "columns", | |
| 362 | − "schema": "public", | |
| 363 | − "table": "executions" | |
| 364 | − }, | |
| 365 | − { | |
| 366 | − "type": "text", | |
| 367 | − "typeSchema": null, | |
| 368 | − "notNull": false, | |
| 369 | − "dimensions": 0, | |
| 370 | − "default": null, | |
| 371 | − "generated": null, | |
| 372 | − "identity": null, | |
| 373 | − "name": "result_json", | |
| 374 | − "entityType": "columns", | |
| 375 | − "schema": "public", | |
| 376 | − "table": "executions" | |
| 377 | − }, | |
| 378 | − { | |
| 379 | − "type": "text", | |
| 380 | − "typeSchema": null, | |
| 381 | − "notNull": false, | |
| 382 | − "dimensions": 0, | |
| 383 | − "default": null, | |
| 384 | − "generated": null, | |
| 385 | − "identity": null, | |
| 386 | − "name": "error_text", | |
| 387 | − "entityType": "columns", | |
| 388 | − "schema": "public", | |
| 389 | − "table": "executions" | |
| 390 | − }, | |
| 391 | − { | |
| 392 | − "type": "text", | |
| 393 | − "typeSchema": null, | |
| 394 | − "notNull": false, | |
| 395 | − "dimensions": 0, | |
| 396 | − "default": null, | |
| 397 | − "generated": null, | |
| 398 | − "identity": null, | |
| 399 | − "name": "logs_json", | |
| 400 | − "entityType": "columns", | |
| 401 | − "schema": "public", | |
| 402 | − "table": "executions" | |
| 403 | − }, | |
| 404 | − { | |
| 405 | − "type": "bigint", | |
| 406 | − "typeSchema": null, | |
| 407 | − "notNull": false, | |
| 408 | − "dimensions": 0, | |
| 409 | − "default": null, | |
| 410 | − "generated": null, | |
| 411 | − "identity": null, | |
| 412 | − "name": "started_at", | |
| 413 | − "entityType": "columns", | |
| 414 | − "schema": "public", | |
| 415 | − "table": "executions" | |
| 416 | − }, | |
| 417 | − { | |
| 418 | − "type": "bigint", | |
| 419 | − "typeSchema": null, | |
| 420 | − "notNull": false, | |
| 421 | − "dimensions": 0, | |
| 422 | − "default": null, | |
| 423 | − "generated": null, | |
| 424 | − "identity": null, | |
| 425 | − "name": "completed_at", | |
| 426 | − "entityType": "columns", | |
| 427 | − "schema": "public", | |
| 428 | − "table": "executions" | |
| 429 | − }, | |
| 430 | − { | |
| 431 | − "type": "bigint", | |
| 432 | − "typeSchema": null, | |
| 433 | − "notNull": true, | |
| 434 | − "dimensions": 0, | |
| 435 | − "default": null, | |
| 436 | − "generated": null, | |
| 437 | − "identity": null, | |
| 438 | − "name": "created_at", | |
| 439 | − "entityType": "columns", | |
| 440 | − "schema": "public", | |
| 441 | − "table": "executions" | |
| 442 | − }, | |
| 443 | − { | |
| 444 | − "type": "bigint", | |
| 445 | − "typeSchema": null, | |
| 446 | − "notNull": true, | |
| 447 | − "dimensions": 0, | |
| 448 | − "default": null, | |
| 449 | − "generated": null, | |
| 450 | − "identity": null, | |
| 451 | − "name": "updated_at", | |
| 452 | − "entityType": "columns", | |
| 453 | − "schema": "public", | |
| 454 | − "table": "executions" | |
| 455 | − }, | |
| 456 | − { | |
| 457 | − "type": "text", | |
| 458 | − "typeSchema": null, | |
| 459 | − "notNull": true, | |
| 460 | − "dimensions": 0, | |
| 461 | − "default": null, | |
| 462 | − "generated": null, | |
| 463 | − "identity": null, | |
| 464 | − "name": "id", | |
| 465 | − "entityType": "columns", | |
| 466 | − "schema": "public", | |
| 467 | − "table": "local_installations" | |
| 468 | − }, | |
| 469 | − { | |
| 470 | − "type": "text", | |
| 471 | − "typeSchema": null, | |
| 472 | − "notNull": true, | |
| 473 | − "dimensions": 0, | |
| 474 | − "default": null, | |
| 475 | − "generated": null, | |
| 476 | − "identity": null, | |
| 477 | − "name": "account_id", | |
| 478 | − "entityType": "columns", | |
| 479 | − "schema": "public", | |
| 480 | − "table": "local_installations" | |
| 481 | − }, | |
| 482 | − { | |
| 483 | − "type": "text", | |
| 484 | − "typeSchema": null, | |
| 485 | − "notNull": true, | |
| 486 | − "dimensions": 0, | |
| 487 | − "default": null, | |
| 488 | − "generated": null, | |
| 489 | − "identity": null, | |
| 490 | − "name": "organization_id", | |
| 491 | − "entityType": "columns", | |
| 492 | − "schema": "public", | |
| 493 | − "table": "local_installations" | |
| 494 | − }, | |
| 495 | − { | |
| 496 | − "type": "text", | |
| 497 | − "typeSchema": null, | |
| 498 | − "notNull": true, | |
| 499 | − "dimensions": 0, | |
| 500 | − "default": null, | |
| 501 | − "generated": null, | |
| 502 | − "identity": null, | |
| 503 | − "name": "workspace_id", | |
| 504 | − "entityType": "columns", | |
| 505 | − "schema": "public", | |
| 506 | − "table": "local_installations" | |
| 507 | − }, | |
| 508 | − { | |
| 509 | − "type": "bigint", | |
| 510 | − "typeSchema": null, | |
| 511 | − "notNull": true, | |
| 512 | − "dimensions": 0, | |
| 513 | − "default": null, | |
| 514 | − "generated": null, | |
| 515 | − "identity": null, | |
| 516 | − "name": "created_at", | |
| 517 | − "entityType": "columns", | |
| 518 | − "schema": "public", | |
| 519 | − "table": "local_installations" | |
| 520 | − }, | |
| 521 | − { | |
| 522 | − "type": "bigint", | |
| 523 | − "typeSchema": null, | |
| 524 | − "notNull": true, | |
| 525 | − "dimensions": 0, | |
| 526 | − "default": null, | |
| 527 | − "generated": null, | |
| 528 | − "identity": null, | |
| 529 | − "name": "updated_at", | |
| 530 | − "entityType": "columns", | |
| 531 | − "schema": "public", | |
| 532 | − "table": "local_installations" | |
| 533 | − }, | |
| 534 | − { | |
| 535 | − "type": "text", | |
| 536 | − "typeSchema": null, | |
| 537 | − "notNull": true, | |
| 538 | − "dimensions": 0, | |
| 539 | − "default": null, | |
| 540 | − "generated": null, | |
| 541 | − "identity": null, | |
| 542 | − "name": "id", | |
| 543 | − "entityType": "columns", | |
| 544 | − "schema": "public", | |
| 545 | − "table": "organization_memberships" | |
| 546 | − }, | |
| 547 | − { | |
| 548 | − "type": "text", | |
| 549 | − "typeSchema": null, | |
| 550 | − "notNull": true, | |
| 551 | − "dimensions": 0, | |
| 552 | − "default": null, | |
| 553 | − "generated": null, | |
| 554 | − "identity": null, | |
| 555 | − "name": "organization_id", | |
| 556 | − "entityType": "columns", | |
| 557 | − "schema": "public", | |
| 558 | − "table": "organization_memberships" | |
| 559 | − }, | |
| 560 | − { | |
| 561 | − "type": "text", | |
| 562 | − "typeSchema": null, | |
| 563 | − "notNull": true, | |
| 564 | − "dimensions": 0, | |
| 565 | − "default": null, | |
| 566 | − "generated": null, | |
| 567 | − "identity": null, | |
| 568 | − "name": "account_id", | |
| 569 | − "entityType": "columns", | |
| 570 | − "schema": "public", | |
| 571 | − "table": "organization_memberships" | |
| 572 | − }, | |
| 573 | − { | |
| 574 | − "type": "text", | |
| 575 | − "typeSchema": null, | |
| 576 | − "notNull": true, | |
| 577 | − "dimensions": 0, | |
| 578 | − "default": null, | |
| 579 | − "generated": null, | |
| 580 | − "identity": null, | |
| 581 | − "name": "role", | |
| 582 | − "entityType": "columns", | |
| 583 | − "schema": "public", | |
| 584 | − "table": "organization_memberships" | |
| 585 | − }, | |
| 586 | − { | |
| 587 | − "type": "text", | |
| 588 | − "typeSchema": null, | |
| 589 | − "notNull": true, | |
| 590 | − "dimensions": 0, | |
| 591 | − "default": null, | |
| 592 | − "generated": null, | |
| 593 | − "identity": null, | |
| 594 | − "name": "status", | |
| 595 | − "entityType": "columns", | |
| 596 | − "schema": "public", | |
| 597 | − "table": "organization_memberships" | |
| 598 | − }, | |
| 599 | − { | |
| 600 | − "type": "boolean", | |
| 601 | − "typeSchema": null, | |
| 602 | − "notNull": true, | |
| 603 | − "dimensions": 0, | |
| 604 | − "default": null, | |
| 605 | − "generated": null, | |
| 606 | − "identity": null, | |
| 607 | − "name": "billable", | |
| 608 | − "entityType": "columns", | |
| 609 | − "schema": "public", | |
| 610 | − "table": "organization_memberships" | |
| 611 | − }, | |
| 612 | − { | |
| 613 | − "type": "text", | |
| 614 | − "typeSchema": null, | |
| 615 | − "notNull": false, | |
| 616 | − "dimensions": 0, | |
| 617 | − "default": null, | |
| 618 | − "generated": null, | |
| 619 | − "identity": null, | |
| 620 | − "name": "invited_by_account_id", | |
| 621 | − "entityType": "columns", | |
| 622 | − "schema": "public", | |
| 623 | − "table": "organization_memberships" | |
| 624 | − }, | |
| 625 | − { | |
| 626 | − "type": "bigint", | |
| 627 | − "typeSchema": null, | |
| 628 | − "notNull": false, | |
| 629 | − "dimensions": 0, | |
| 630 | − "default": null, | |
| 631 | − "generated": null, | |
| 632 | − "identity": null, | |
| 633 | − "name": "joined_at", | |
| 634 | − "entityType": "columns", | |
| 635 | − "schema": "public", | |
| 636 | − "table": "organization_memberships" | |
| 637 | − }, | |
| 638 | − { | |
| 639 | − "type": "bigint", | |
| 640 | − "typeSchema": null, | |
| 641 | − "notNull": true, | |
| 642 | − "dimensions": 0, | |
| 643 | − "default": null, | |
| 644 | − "generated": null, | |
| 645 | − "identity": null, | |
| 646 | − "name": "created_at", | |
| 647 | − "entityType": "columns", | |
| 648 | − "schema": "public", | |
| 649 | − "table": "organization_memberships" | |
| 650 | − }, | |
| 651 | − { | |
| 652 | − "type": "bigint", | |
| 653 | − "typeSchema": null, | |
| 654 | − "notNull": true, | |
| 655 | − "dimensions": 0, | |
| 656 | − "default": null, | |
| 657 | − "generated": null, | |
| 658 | − "identity": null, | |
| 659 | − "name": "updated_at", | |
| 660 | − "entityType": "columns", | |
| 661 | − "schema": "public", | |
| 662 | − "table": "organization_memberships" | |
| 663 | − }, | |
| 664 | − { | |
| 665 | − "type": "text", | |
| 666 | − "typeSchema": null, | |
| 667 | − "notNull": true, | |
| 668 | − "dimensions": 0, | |
| 669 | − "default": null, | |
| 670 | − "generated": null, | |
| 671 | − "identity": null, | |
| 672 | − "name": "id", | |
| 673 | − "entityType": "columns", | |
| 674 | − "schema": "public", | |
| 675 | − "table": "organizations" | |
| 676 | − }, | |
| 677 | − { | |
| 678 | − "type": "text", | |
| 679 | − "typeSchema": null, | |
| 680 | − "notNull": true, | |
| 681 | − "dimensions": 0, | |
| 682 | − "default": null, | |
| 683 | − "generated": null, | |
| 684 | − "identity": null, | |
| 685 | − "name": "slug", | |
| 686 | − "entityType": "columns", | |
| 687 | − "schema": "public", | |
| 688 | − "table": "organizations" | |
| 689 | − }, | |
| 690 | − { | |
| 691 | − "type": "text", | |
| 692 | − "typeSchema": null, | |
| 693 | − "notNull": true, | |
| 694 | − "dimensions": 0, | |
| 695 | − "default": null, | |
| 696 | − "generated": null, | |
| 697 | − "identity": null, | |
| 698 | − "name": "name", | |
| 699 | − "entityType": "columns", | |
| 700 | − "schema": "public", | |
| 701 | − "table": "organizations" | |
| 702 | − }, | |
| 703 | − { | |
| 704 | − "type": "text", | |
| 705 | − "typeSchema": null, | |
| 706 | − "notNull": true, | |
| 707 | − "dimensions": 0, | |
| 708 | − "default": null, | |
| 709 | − "generated": null, | |
| 710 | − "identity": null, | |
| 711 | − "name": "status", | |
| 712 | − "entityType": "columns", | |
| 713 | − "schema": "public", | |
| 714 | − "table": "organizations" | |
| 715 | − }, | |
| 716 | − { | |
| 717 | − "type": "text", | |
| 718 | − "typeSchema": null, | |
| 719 | − "notNull": false, | |
| 720 | − "dimensions": 0, | |
| 721 | − "default": null, | |
| 722 | − "generated": null, | |
| 723 | − "identity": null, | |
| 724 | − "name": "created_by_account_id", | |
| 725 | − "entityType": "columns", | |
| 726 | − "schema": "public", | |
| 727 | − "table": "organizations" | |
| 728 | − }, | |
| 729 | − { | |
| 730 | − "type": "bigint", | |
| 731 | − "typeSchema": null, | |
| 732 | − "notNull": true, | |
| 733 | − "dimensions": 0, | |
| 734 | − "default": null, | |
| 735 | − "generated": null, | |
| 736 | − "identity": null, | |
| 737 | − "name": "created_at", | |
| 738 | − "entityType": "columns", | |
| 739 | − "schema": "public", | |
| 740 | − "table": "organizations" | |
| 741 | − }, | |
| 742 | − { | |
| 743 | − "type": "bigint", | |
| 744 | − "typeSchema": null, | |
| 745 | − "notNull": true, | |
| 746 | − "dimensions": 0, | |
| 747 | − "default": null, | |
| 748 | − "generated": null, | |
| 749 | − "identity": null, | |
| 750 | − "name": "updated_at", | |
| 751 | − "entityType": "columns", | |
| 752 | − "schema": "public", | |
| 753 | − "table": "organizations" | |
| 754 | − }, | |
| 755 | − { | |
| 756 | − "type": "text", | |
| 757 | − "typeSchema": null, | |
| 758 | − "notNull": true, | |
| 759 | − "dimensions": 0, | |
| 760 | − "default": null, | |
| 761 | − "generated": null, | |
| 762 | − "identity": null, | |
| 763 | − "name": "id", | |
| 764 | − "entityType": "columns", | |
| 765 | − "schema": "public", | |
| 766 | − "table": "policies" | |
| 767 | − }, | |
| 768 | − { | |
| 769 | − "type": "text", | |
| 770 | − "typeSchema": null, | |
| 771 | − "notNull": true, | |
| 772 | − "dimensions": 0, | |
| 773 | − "default": null, | |
| 774 | − "generated": null, | |
| 775 | − "identity": null, | |
| 776 | − "name": "workspace_id", | |
| 777 | − "entityType": "columns", | |
| 778 | − "schema": "public", | |
| 779 | − "table": "policies" | |
| 780 | − }, | |
| 781 | − { | |
| 782 | − "type": "text", | |
| 783 | − "typeSchema": null, | |
| 784 | − "notNull": false, | |
| 785 | − "dimensions": 0, | |
| 786 | − "default": null, | |
| 787 | − "generated": null, | |
| 788 | − "identity": null, | |
| 789 | − "name": "target_account_id", | |
| 790 | − "entityType": "columns", | |
| 791 | − "schema": "public", | |
| 792 | − "table": "policies" | |
| 793 | − }, | |
| 794 | − { | |
| 795 | − "type": "text", | |
| 796 | − "typeSchema": null, | |
| 797 | − "notNull": false, | |
| 798 | − "dimensions": 0, | |
| 799 | − "default": null, | |
| 800 | − "generated": null, | |
| 801 | − "identity": null, | |
| 802 | − "name": "client_id", | |
| 803 | − "entityType": "columns", | |
| 804 | − "schema": "public", | |
| 805 | − "table": "policies" | |
| 806 | − }, | |
| 807 | − { | |
| 808 | − "type": "text", | |
| 809 | − "typeSchema": null, | |
| 810 | − "notNull": true, | |
| 811 | − "dimensions": 0, | |
| 812 | − "default": null, | |
| 813 | − "generated": null, | |
| 814 | − "identity": null, | |
| 815 | − "name": "resource_type", | |
| 816 | − "entityType": "columns", | |
| 817 | − "schema": "public", | |
| 818 | − "table": "policies" | |
| 819 | − }, | |
| 820 | − { | |
| 821 | − "type": "text", | |
| 822 | − "typeSchema": null, | |
| 823 | − "notNull": true, | |
| 824 | − "dimensions": 0, | |
| 825 | − "default": null, | |
| 826 | − "generated": null, | |
| 827 | − "identity": null, | |
| 828 | − "name": "resource_pattern", | |
| 829 | − "entityType": "columns", | |
| 830 | − "schema": "public", | |
| 831 | − "table": "policies" | |
| 832 | − }, | |
| 833 | − { | |
| 834 | − "type": "text", | |
| 835 | − "typeSchema": null, | |
| 836 | − "notNull": true, | |
| 837 | − "dimensions": 0, | |
| 838 | − "default": null, | |
| 839 | − "generated": null, | |
| 840 | − "identity": null, | |
| 841 | − "name": "match_type", | |
| 842 | − "entityType": "columns", | |
| 843 | − "schema": "public", | |
| 844 | − "table": "policies" | |
| 845 | − }, | |
| 846 | − { | |
| 847 | − "type": "text", | |
| 848 | − "typeSchema": null, | |
| 849 | − "notNull": true, | |
| 850 | − "dimensions": 0, | |
| 851 | − "default": null, | |
| 852 | − "generated": null, | |
| 853 | − "identity": null, | |
| 854 | − "name": "effect", | |
| 855 | − "entityType": "columns", | |
| 856 | − "schema": "public", | |
| 857 | − "table": "policies" | |
| 858 | − }, | |
| 859 | − { | |
| 860 | − "type": "text", | |
| 861 | − "typeSchema": null, | |
| 862 | − "notNull": true, | |
| 863 | − "dimensions": 0, | |
| 864 | − "default": null, | |
| 865 | − "generated": null, | |
| 866 | − "identity": null, | |
| 867 | − "name": "approval_mode", | |
| 868 | − "entityType": "columns", | |
| 869 | − "schema": "public", | |
| 870 | − "table": "policies" | |
| 871 | − }, | |
| 872 | − { | |
| 873 | − "type": "text", | |
| 874 | − "typeSchema": null, | |
| 875 | − "notNull": false, | |
| 876 | − "dimensions": 0, | |
| 877 | − "default": null, | |
| 878 | − "generated": null, | |
| 879 | − "identity": null, | |
| 880 | − "name": "argument_conditions_json", | |
| 881 | − "entityType": "columns", | |
| 882 | − "schema": "public", | |
| 883 | − "table": "policies" | |
| 884 | − }, | |
| 885 | − { | |
| 886 | − "type": "bigint", | |
| 887 | − "typeSchema": null, | |
| 888 | − "notNull": true, | |
| 889 | − "dimensions": 0, | |
| 890 | − "default": null, | |
| 891 | − "generated": null, | |
| 892 | − "identity": null, | |
| 893 | − "name": "priority", | |
| 894 | − "entityType": "columns", | |
| 895 | − "schema": "public", | |
| 896 | − "table": "policies" | |
| 897 | − }, | |
| 898 | − { | |
| 899 | − "type": "boolean", | |
| 900 | − "typeSchema": null, | |
| 901 | − "notNull": true, | |
| 902 | − "dimensions": 0, | |
| 903 | − "default": null, | |
| 904 | − "generated": null, | |
| 905 | − "identity": null, | |
| 906 | − "name": "enabled", | |
| 907 | − "entityType": "columns", | |
| 908 | − "schema": "public", | |
| 909 | − "table": "policies" | |
| 910 | − }, | |
| 911 | − { | |
| 912 | − "type": "bigint", | |
| 913 | − "typeSchema": null, | |
| 914 | − "notNull": true, | |
| 915 | − "dimensions": 0, | |
| 916 | − "default": null, | |
| 917 | − "generated": null, | |
| 918 | − "identity": null, | |
| 919 | − "name": "created_at", | |
| 920 | − "entityType": "columns", | |
| 921 | − "schema": "public", | |
| 922 | − "table": "policies" | |
| 923 | − }, | |
| 924 | − { | |
| 925 | − "type": "bigint", | |
| 926 | − "typeSchema": null, | |
| 927 | − "notNull": true, | |
| 928 | − "dimensions": 0, | |
| 929 | − "default": null, | |
| 930 | − "generated": null, | |
| 931 | − "identity": null, | |
| 932 | − "name": "updated_at", | |
| 933 | − "entityType": "columns", | |
| 934 | − "schema": "public", | |
| 935 | − "table": "policies" | |
| 936 | − }, | |
| 937 | − { | |
| 938 | − "type": "text", | |
| 939 | − "typeSchema": null, | |
| 940 | − "notNull": true, | |
| 941 | − "dimensions": 0, | |
| 942 | − "default": null, | |
| 943 | − "generated": null, | |
| 944 | − "identity": null, | |
| 945 | − "name": "id", | |
| 946 | − "entityType": "columns", | |
| 947 | − "schema": "public", | |
| 948 | − "table": "secret_materials" | |
| 949 | − }, | |
| 950 | − { | |
| 951 | − "type": "text", | |
| 952 | − "typeSchema": null, | |
| 953 | − "notNull": true, | |
| 954 | − "dimensions": 0, | |
| 955 | − "default": null, | |
| 956 | − "generated": null, | |
| 957 | − "identity": null, | |
| 958 | − "name": "purpose", | |
| 959 | − "entityType": "columns", | |
| 960 | − "schema": "public", | |
| 961 | − "table": "secret_materials" | |
| 962 | − }, | |
| 963 | − { | |
| 964 | − "type": "text", | |
| 965 | − "typeSchema": null, | |
| 966 | − "notNull": true, | |
| 967 | − "dimensions": 0, | |
| 968 | − "default": null, | |
| 969 | − "generated": null, | |
| 970 | − "identity": null, | |
| 971 | − "name": "value", | |
| 972 | − "entityType": "columns", | |
| 973 | − "schema": "public", | |
| 974 | − "table": "secret_materials" | |
| 975 | − }, | |
| 976 | − { | |
| 977 | − "type": "bigint", | |
| 978 | − "typeSchema": null, | |
| 979 | − "notNull": true, | |
| 980 | − "dimensions": 0, | |
| 981 | − "default": null, | |
| 982 | − "generated": null, | |
| 983 | − "identity": null, | |
| 984 | − "name": "created_at", | |
| 985 | − "entityType": "columns", | |
| 986 | − "schema": "public", | |
| 987 | − "table": "secret_materials" | |
| 988 | − }, | |
| 989 | − { | |
| 990 | − "type": "bigint", | |
| 991 | − "typeSchema": null, | |
| 992 | − "notNull": true, | |
| 993 | − "dimensions": 0, | |
| 994 | − "default": null, | |
| 995 | − "generated": null, | |
| 996 | − "identity": null, | |
| 997 | − "name": "updated_at", | |
| 998 | − "entityType": "columns", | |
| 999 | − "schema": "public", | |
| 1000 | − "table": "secret_materials" | |
| 1001 | − }, | |
| 1002 | − { | |
| 1003 | − "type": "text", | |
| 1004 | − "typeSchema": null, | |
| 1005 | − "notNull": true, | |
| 1006 | − "dimensions": 0, | |
| 1007 | − "default": null, | |
| 1008 | − "generated": null, | |
| 1009 | − "identity": null, | |
| 1010 | − "name": "id", | |
| 1011 | − "entityType": "columns", | |
| 1012 | − "schema": "public", | |
| 1013 | − "table": "source_auth_sessions" | |
| 1014 | − }, | |
| 1015 | − { | |
| 1016 | − "type": "text", | |
| 1017 | − "typeSchema": null, | |
| 1018 | − "notNull": true, | |
| 1019 | − "dimensions": 0, | |
| 1020 | − "default": null, | |
| 1021 | − "generated": null, | |
| 1022 | − "identity": null, | |
| 1023 | − "name": "workspace_id", | |
| 1024 | − "entityType": "columns", | |
| 1025 | − "schema": "public", | |
| 1026 | − "table": "source_auth_sessions" | |
| 1027 | − }, | |
| 1028 | − { | |
| 1029 | − "type": "text", | |
| 1030 | − "typeSchema": null, | |
| 1031 | − "notNull": true, | |
| 1032 | − "dimensions": 0, | |
| 1033 | − "default": null, | |
| 1034 | − "generated": null, | |
| 1035 | − "identity": null, | |
| 1036 | − "name": "source_id", | |
| 1037 | − "entityType": "columns", | |
| 1038 | − "schema": "public", | |
| 1039 | − "table": "source_auth_sessions" | |
| 1040 | − }, | |
| 1041 | − { | |
| 1042 | − "type": "text", | |
| 1043 | − "typeSchema": null, | |
| 1044 | − "notNull": false, | |
| 1045 | − "dimensions": 0, | |
| 1046 | − "default": null, | |
| 1047 | − "generated": null, | |
| 1048 | − "identity": null, | |
| 1049 | − "name": "execution_id", | |
| 1050 | − "entityType": "columns", | |
| 1051 | − "schema": "public", | |
| 1052 | − "table": "source_auth_sessions" | |
| 1053 | − }, | |
| 1054 | − { | |
| 1055 | − "type": "text", | |
| 1056 | − "typeSchema": null, | |
| 1057 | − "notNull": false, | |
| 1058 | − "dimensions": 0, | |
| 1059 | − "default": null, | |
| 1060 | − "generated": null, | |
| 1061 | − "identity": null, | |
| 1062 | − "name": "interaction_id", | |
| 1063 | − "entityType": "columns", | |
| 1064 | − "schema": "public", | |
| 1065 | − "table": "source_auth_sessions" | |
| 1066 | − }, | |
| 1067 | − { | |
| 1068 | − "type": "text", | |
| 1069 | − "typeSchema": null, | |
| 1070 | − "notNull": true, | |
| 1071 | − "dimensions": 0, | |
| 1072 | − "default": null, | |
| 1073 | − "generated": null, | |
| 1074 | − "identity": null, | |
| 1075 | − "name": "strategy", | |
| 1076 | − "entityType": "columns", | |
| 1077 | − "schema": "public", | |
| 1078 | − "table": "source_auth_sessions" | |
| 1079 | − }, | |
| 1080 | − { | |
| 1081 | − "type": "text", | |
| 1082 | − "typeSchema": null, | |
| 1083 | − "notNull": true, | |
| 1084 | − "dimensions": 0, | |
| 1085 | − "default": null, | |
| 1086 | − "generated": null, | |
| 1087 | − "identity": null, | |
| 1088 | − "name": "status", | |
| 1089 | − "entityType": "columns", | |
| 1090 | − "schema": "public", | |
| 1091 | − "table": "source_auth_sessions" | |
| 1092 | − }, | |
| 1093 | − { | |
| 1094 | − "type": "text", | |
| 1095 | − "typeSchema": null, | |
| 1096 | − "notNull": true, | |
| 1097 | − "dimensions": 0, | |
| 1098 | − "default": null, | |
| 1099 | − "generated": null, | |
| 1100 | − "identity": null, | |
| 1101 | − "name": "endpoint", | |
| 1102 | − "entityType": "columns", | |
| 1103 | − "schema": "public", | |
| 1104 | − "table": "source_auth_sessions" | |
| 1105 | − }, | |
| 1106 | − { | |
| 1107 | − "type": "text", | |
| 1108 | − "typeSchema": null, | |
| 1109 | − "notNull": true, | |
| 1110 | − "dimensions": 0, | |
| 1111 | − "default": null, | |
| 1112 | − "generated": null, | |
| 1113 | − "identity": null, | |
| 1114 | − "name": "state", | |
| 1115 | − "entityType": "columns", | |
| 1116 | − "schema": "public", | |
| 1117 | − "table": "source_auth_sessions" | |
| 1118 | − }, | |
| 1119 | − { | |
| 1120 | − "type": "text", | |
| 1121 | − "typeSchema": null, | |
| 1122 | − "notNull": true, | |
| 1123 | − "dimensions": 0, | |
| 1124 | − "default": null, | |
| 1125 | − "generated": null, | |
| 1126 | − "identity": null, | |
| 1127 | − "name": "redirect_uri", | |
| 1128 | − "entityType": "columns", | |
| 1129 | − "schema": "public", | |
| 1130 | − "table": "source_auth_sessions" | |
| 1131 | − }, | |
| 1132 | − { | |
| 1133 | − "type": "text", | |
| 1134 | − "typeSchema": null, | |
| 1135 | − "notNull": false, | |
| 1136 | − "dimensions": 0, | |
| 1137 | − "default": null, | |
| 1138 | − "generated": null, | |
| 1139 | − "identity": null, | |
| 1140 | − "name": "scope", | |
| 1141 | − "entityType": "columns", | |
| 1142 | − "schema": "public", | |
| 1143 | − "table": "source_auth_sessions" | |
| 1144 | − }, | |
| 1145 | − { | |
| 1146 | − "type": "text", | |
| 1147 | − "typeSchema": null, | |
| 1148 | − "notNull": false, | |
| 1149 | − "dimensions": 0, | |
| 1150 | − "default": null, | |
| 1151 | − "generated": null, | |
| 1152 | − "identity": null, | |
| 1153 | − "name": "resource_metadata_url", | |
| 1154 | − "entityType": "columns", | |
| 1155 | − "schema": "public", | |
| 1156 | − "table": "source_auth_sessions" | |
| 1157 | − }, | |
| 1158 | − { | |
| 1159 | − "type": "text", | |
| 1160 | − "typeSchema": null, | |
| 1161 | − "notNull": false, | |
| 1162 | − "dimensions": 0, | |
| 1163 | − "default": null, | |
| 1164 | − "generated": null, | |
| 1165 | − "identity": null, | |
| 1166 | − "name": "authorization_server_url", | |
| 1167 | − "entityType": "columns", | |
| 1168 | − "schema": "public", | |
| 1169 | − "table": "source_auth_sessions" | |
| 1170 | − }, | |
| 1171 | − { | |
| 1172 | − "type": "text", | |
| 1173 | − "typeSchema": null, | |
| 1174 | − "notNull": false, | |
| 1175 | − "dimensions": 0, | |
| 1176 | − "default": null, | |
| 1177 | − "generated": null, | |
| 1178 | − "identity": null, | |
| 1179 | − "name": "resource_metadata_json", | |
| 1180 | − "entityType": "columns", | |
| 1181 | − "schema": "public", | |
| 1182 | − "table": "source_auth_sessions" | |
| 1183 | − }, | |
| 1184 | − { | |
| 1185 | − "type": "text", | |
| 1186 | − "typeSchema": null, | |
| 1187 | − "notNull": false, | |
| 1188 | − "dimensions": 0, | |
| 1189 | − "default": null, | |
| 1190 | − "generated": null, | |
| 1191 | − "identity": null, | |
| 1192 | − "name": "authorization_server_metadata_json", | |
| 1193 | − "entityType": "columns", | |
| 1194 | − "schema": "public", | |
| 1195 | − "table": "source_auth_sessions" | |
| 1196 | − }, | |
| 1197 | − { | |
| 1198 | − "type": "text", | |
| 1199 | − "typeSchema": null, | |
| 1200 | − "notNull": false, | |
| 1201 | − "dimensions": 0, | |
| 1202 | − "default": null, | |
| 1203 | − "generated": null, | |
| 1204 | − "identity": null, | |
| 1205 | − "name": "client_information_json", | |
| 1206 | − "entityType": "columns", | |
| 1207 | − "schema": "public", | |
| 1208 | − "table": "source_auth_sessions" | |
| 1209 | − }, | |
| 1210 | − { | |
| 1211 | − "type": "text", | |
| 1212 | − "typeSchema": null, | |
| 1213 | − "notNull": false, | |
| 1214 | − "dimensions": 0, | |
| 1215 | − "default": null, | |
| 1216 | − "generated": null, | |
| 1217 | − "identity": null, | |
| 1218 | − "name": "code_verifier", | |
| 1219 | − "entityType": "columns", | |
| 1220 | − "schema": "public", | |
| 1221 | − "table": "source_auth_sessions" | |
| 1222 | − }, | |
| 1223 | − { | |
| 1224 | − "type": "text", | |
| 1225 | − "typeSchema": null, | |
| 1226 | − "notNull": false, | |
| 1227 | − "dimensions": 0, | |
| 1228 | − "default": null, | |
| 1229 | − "generated": null, | |
| 1230 | − "identity": null, | |
| 1231 | − "name": "authorization_url", | |
| 1232 | − "entityType": "columns", | |
| 1233 | − "schema": "public", | |
| 1234 | − "table": "source_auth_sessions" | |
| 1235 | − }, | |
| 1236 | − { | |
| 1237 | − "type": "text", | |
| 1238 | − "typeSchema": null, | |
| 1239 | − "notNull": false, | |
| 1240 | − "dimensions": 0, | |
| 1241 | − "default": null, | |
| 1242 | − "generated": null, | |
| 1243 | − "identity": null, | |
| 1244 | − "name": "error_text", | |
| 1245 | − "entityType": "columns", | |
| 1246 | − "schema": "public", | |
| 1247 | − "table": "source_auth_sessions" | |
| 1248 | − }, | |
| 1249 | − { | |
| 1250 | − "type": "bigint", | |
| 1251 | − "typeSchema": null, | |
| 1252 | − "notNull": false, | |
| 1253 | − "dimensions": 0, | |
| 1254 | − "default": null, | |
| 1255 | − "generated": null, | |
| 1256 | − "identity": null, | |
| 1257 | − "name": "completed_at", | |
| 1258 | − "entityType": "columns", | |
| 1259 | − "schema": "public", | |
| 1260 | − "table": "source_auth_sessions" | |
| 1261 | − }, | |
| 1262 | − { | |
| 1263 | − "type": "bigint", | |
| 1264 | − "typeSchema": null, | |
| 1265 | − "notNull": true, | |
| 1266 | − "dimensions": 0, | |
| 1267 | − "default": null, | |
| 1268 | − "generated": null, | |
| 1269 | − "identity": null, | |
| 1270 | − "name": "created_at", | |
| 1271 | − "entityType": "columns", | |
| 1272 | − "schema": "public", | |
| 1273 | − "table": "source_auth_sessions" | |
| 1274 | − }, | |
| 1275 | − { | |
| 1276 | − "type": "bigint", | |
| 1277 | − "typeSchema": null, | |
| 1278 | − "notNull": true, | |
| 1279 | − "dimensions": 0, | |
| 1280 | − "default": null, | |
| 1281 | − "generated": null, | |
| 1282 | − "identity": null, | |
| 1283 | − "name": "updated_at", | |
| 1284 | − "entityType": "columns", | |
| 1285 | − "schema": "public", | |
| 1286 | − "table": "source_auth_sessions" | |
| 1287 | − }, | |
| 1288 | − { | |
| 1289 | − "type": "text", | |
| 1290 | − "typeSchema": null, | |
| 1291 | − "notNull": true, | |
| 1292 | − "dimensions": 0, | |
| 1293 | − "default": null, | |
| 1294 | − "generated": null, | |
| 1295 | − "identity": null, | |
| 1296 | − "name": "workspace_id", | |
| 1297 | − "entityType": "columns", | |
| 1298 | − "schema": "public", | |
| 1299 | − "table": "source_credential_bindings" | |
| 1300 | − }, | |
| 1301 | − { | |
| 1302 | − "type": "text", | |
| 1303 | − "typeSchema": null, | |
| 1304 | − "notNull": true, | |
| 1305 | − "dimensions": 0, | |
| 1306 | − "default": null, | |
| 1307 | − "generated": null, | |
| 1308 | − "identity": null, | |
| 1309 | − "name": "source_id", | |
| 1310 | − "entityType": "columns", | |
| 1311 | − "schema": "public", | |
| 1312 | − "table": "source_credential_bindings" | |
| 1313 | − }, | |
| 1314 | − { | |
| 1315 | − "type": "text", | |
| 1316 | − "typeSchema": null, | |
| 1317 | − "notNull": false, | |
| 1318 | − "dimensions": 0, | |
| 1319 | − "default": null, | |
| 1320 | − "generated": null, | |
| 1321 | − "identity": null, | |
| 1322 | − "name": "token_provider_id", | |
| 1323 | − "entityType": "columns", | |
| 1324 | − "schema": "public", | |
| 1325 | − "table": "source_credential_bindings" | |
| 1326 | − }, | |
| 1327 | − { | |
| 1328 | − "type": "text", | |
| 1329 | − "typeSchema": null, | |
| 1330 | − "notNull": false, | |
| 1331 | − "dimensions": 0, | |
| 1332 | − "default": null, | |
| 1333 | − "generated": null, | |
| 1334 | − "identity": null, | |
| 1335 | − "name": "token_handle", | |
| 1336 | − "entityType": "columns", | |
| 1337 | − "schema": "public", | |
| 1338 | − "table": "source_credential_bindings" | |
| 1339 | − }, | |
| 1340 | − { | |
| 1341 | − "type": "text", | |
| 1342 | − "typeSchema": null, | |
| 1343 | − "notNull": false, | |
| 1344 | − "dimensions": 0, | |
| 1345 | − "default": null, | |
| 1346 | − "generated": null, | |
| 1347 | − "identity": null, | |
| 1348 | − "name": "refresh_token_provider_id", | |
| 1349 | − "entityType": "columns", | |
| 1350 | − "schema": "public", | |
| 1351 | − "table": "source_credential_bindings" | |
| 1352 | − }, | |
| 1353 | − { | |
| 1354 | − "type": "text", | |
| 1355 | − "typeSchema": null, | |
| 1356 | − "notNull": false, | |
| 1357 | − "dimensions": 0, | |
| 1358 | − "default": null, | |
| 1359 | − "generated": null, | |
| 1360 | − "identity": null, | |
| 1361 | − "name": "refresh_token_handle", | |
| 1362 | − "entityType": "columns", | |
| 1363 | − "schema": "public", | |
| 1364 | − "table": "source_credential_bindings" | |
| 1365 | − }, | |
| 1366 | − { | |
| 1367 | − "type": "bigint", | |
| 1368 | − "typeSchema": null, | |
| 1369 | − "notNull": true, | |
| 1370 | − "dimensions": 0, | |
| 1371 | − "default": null, | |
| 1372 | − "generated": null, | |
| 1373 | − "identity": null, | |
| 1374 | − "name": "created_at", | |
| 1375 | − "entityType": "columns", | |
| 1376 | − "schema": "public", | |
| 1377 | − "table": "source_credential_bindings" | |
| 1378 | − }, | |
| 1379 | − { | |
| 1380 | − "type": "bigint", | |
| 1381 | − "typeSchema": null, | |
| 1382 | − "notNull": true, | |
| 1383 | − "dimensions": 0, | |
| 1384 | − "default": null, | |
| 1385 | − "generated": null, | |
| 1386 | − "identity": null, | |
| 1387 | − "name": "updated_at", | |
| 1388 | − "entityType": "columns", | |
| 1389 | − "schema": "public", | |
| 1390 | − "table": "source_credential_bindings" | |
| 1391 | − }, | |
| 1392 | − { | |
| 1393 | − "type": "text", | |
| 1394 | − "typeSchema": null, | |
| 1395 | − "notNull": true, | |
| 1396 | − "dimensions": 0, | |
| 1397 | − "default": null, | |
| 1398 | − "generated": null, | |
| 1399 | − "identity": null, | |
| 1400 | − "name": "workspace_id", | |
| 1401 | − "entityType": "columns", | |
| 1402 | − "schema": "public", | |
| 1403 | − "table": "sources" | |
| 1404 | − }, | |
| 1405 | − { | |
| 1406 | − "type": "text", | |
| 1407 | − "typeSchema": null, | |
| 1408 | − "notNull": true, | |
| 1409 | − "dimensions": 0, | |
| 1410 | − "default": null, | |
| 1411 | − "generated": null, | |
| 1412 | − "identity": null, | |
| 1413 | − "name": "source_id", | |
| 1414 | − "entityType": "columns", | |
| 1415 | − "schema": "public", | |
| 1416 | − "table": "sources" | |
| 1417 | − }, | |
| 1418 | − { | |
| 1419 | − "type": "text", | |
| 1420 | − "typeSchema": null, | |
| 1421 | − "notNull": true, | |
| 1422 | − "dimensions": 0, | |
| 1423 | − "default": null, | |
| 1424 | − "generated": null, | |
| 1425 | − "identity": null, | |
| 1426 | − "name": "name", | |
| 1427 | − "entityType": "columns", | |
| 1428 | − "schema": "public", | |
| 1429 | − "table": "sources" | |
| 1430 | − }, | |
| 1431 | − { | |
| 1432 | − "type": "text", | |
| 1433 | − "typeSchema": null, | |
| 1434 | − "notNull": true, | |
| 1435 | − "dimensions": 0, | |
| 1436 | − "default": null, | |
| 1437 | − "generated": null, | |
| 1438 | − "identity": null, | |
| 1439 | − "name": "kind", | |
| 1440 | − "entityType": "columns", | |
| 1441 | − "schema": "public", | |
| 1442 | − "table": "sources" | |
| 1443 | − }, | |
| 1444 | − { | |
| 1445 | − "type": "text", | |
| 1446 | − "typeSchema": null, | |
| 1447 | − "notNull": true, | |
| 1448 | − "dimensions": 0, | |
| 1449 | − "default": null, | |
| 1450 | − "generated": null, | |
| 1451 | − "identity": null, | |
| 1452 | − "name": "endpoint", | |
| 1453 | − "entityType": "columns", | |
| 1454 | − "schema": "public", | |
| 1455 | − "table": "sources" | |
| 1456 | − }, | |
| 1457 | − { | |
| 1458 | − "type": "text", | |
| 1459 | − "typeSchema": null, | |
| 1460 | − "notNull": true, | |
| 1461 | − "dimensions": 0, | |
| 1462 | − "default": null, | |
| 1463 | − "generated": null, | |
| 1464 | − "identity": null, | |
| 1465 | − "name": "status", | |
| 1466 | − "entityType": "columns", | |
| 1467 | − "schema": "public", | |
| 1468 | − "table": "sources" | |
| 1469 | − }, | |
| 1470 | − { | |
| 1471 | − "type": "boolean", | |
| 1472 | − "typeSchema": null, | |
| 1473 | − "notNull": true, | |
| 1474 | − "dimensions": 0, | |
| 1475 | − "default": null, | |
| 1476 | − "generated": null, | |
| 1477 | − "identity": null, | |
| 1478 | − "name": "enabled", | |
| 1479 | − "entityType": "columns", | |
| 1480 | − "schema": "public", | |
| 1481 | − "table": "sources" | |
| 1482 | − }, | |
| 1483 | − { | |
| 1484 | − "type": "text", | |
| 1485 | − "typeSchema": null, | |
| 1486 | − "notNull": false, | |
| 1487 | − "dimensions": 0, | |
| 1488 | − "default": null, | |
| 1489 | − "generated": null, | |
| 1490 | − "identity": null, | |
| 1491 | − "name": "namespace", | |
| 1492 | − "entityType": "columns", | |
| 1493 | − "schema": "public", | |
| 1494 | − "table": "sources" | |
| 1495 | − }, | |
| 1496 | − { | |
| 1497 | − "type": "text", | |
| 1498 | − "typeSchema": null, | |
| 1499 | − "notNull": false, | |
| 1500 | − "dimensions": 0, | |
| 1501 | − "default": null, | |
| 1502 | − "generated": null, | |
| 1503 | − "identity": null, | |
| 1504 | − "name": "transport", | |
| 1505 | − "entityType": "columns", | |
| 1506 | − "schema": "public", | |
| 1507 | − "table": "sources" | |
| 1508 | − }, | |
| 1509 | − { | |
| 1510 | − "type": "text", | |
| 1511 | − "typeSchema": null, | |
| 1512 | − "notNull": false, | |
| 1513 | − "dimensions": 0, | |
| 1514 | − "default": null, | |
| 1515 | − "generated": null, | |
| 1516 | − "identity": null, | |
| 1517 | − "name": "query_params_json", | |
| 1518 | − "entityType": "columns", | |
| 1519 | − "schema": "public", | |
| 1520 | − "table": "sources" | |
| 1521 | − }, | |
| 1522 | − { | |
| 1523 | − "type": "text", | |
| 1524 | − "typeSchema": null, | |
| 1525 | − "notNull": false, | |
| 1526 | − "dimensions": 0, | |
| 1527 | − "default": null, | |
| 1528 | − "generated": null, | |
| 1529 | − "identity": null, | |
| 1530 | − "name": "headers_json", | |
| 1531 | − "entityType": "columns", | |
| 1532 | − "schema": "public", | |
| 1533 | − "table": "sources" | |
| 1534 | − }, | |
| 1535 | − { | |
| 1536 | − "type": "text", | |
| 1537 | − "typeSchema": null, | |
| 1538 | − "notNull": false, | |
| 1539 | − "dimensions": 0, | |
| 1540 | − "default": null, | |
| 1541 | − "generated": null, | |
| 1542 | − "identity": null, | |
| 1543 | − "name": "spec_url", | |
| 1544 | − "entityType": "columns", | |
| 1545 | − "schema": "public", | |
| 1546 | − "table": "sources" | |
| 1547 | − }, | |
| 1548 | − { | |
| 1549 | − "type": "text", | |
| 1550 | − "typeSchema": null, | |
| 1551 | − "notNull": false, | |
| 1552 | − "dimensions": 0, | |
| 1553 | − "default": null, | |
| 1554 | − "generated": null, | |
| 1555 | − "identity": null, | |
| 1556 | − "name": "default_headers_json", | |
| 1557 | − "entityType": "columns", | |
| 1558 | − "schema": "public", | |
| 1559 | − "table": "sources" | |
| 1560 | − }, | |
| 1561 | − { | |
| 1562 | − "type": "text", | |
| 1563 | − "typeSchema": null, | |
| 1564 | − "notNull": true, | |
| 1565 | − "dimensions": 0, | |
| 1566 | − "default": null, | |
| 1567 | − "generated": null, | |
| 1568 | − "identity": null, | |
| 1569 | − "name": "auth_kind", | |
| 1570 | − "entityType": "columns", | |
| 1571 | − "schema": "public", | |
| 1572 | − "table": "sources" | |
| 1573 | − }, | |
| 1574 | − { | |
| 1575 | − "type": "text", | |
| 1576 | − "typeSchema": null, | |
| 1577 | − "notNull": false, | |
| 1578 | − "dimensions": 0, | |
| 1579 | − "default": null, | |
| 1580 | − "generated": null, | |
| 1581 | − "identity": null, | |
| 1582 | − "name": "auth_header_name", | |
| 1583 | − "entityType": "columns", | |
| 1584 | − "schema": "public", | |
| 1585 | − "table": "sources" | |
| 1586 | − }, | |
| 1587 | − { | |
| 1588 | − "type": "text", | |
| 1589 | − "typeSchema": null, | |
| 1590 | − "notNull": false, | |
| 1591 | − "dimensions": 0, | |
| 1592 | − "default": null, | |
| 1593 | − "generated": null, | |
| 1594 | − "identity": null, | |
| 1595 | − "name": "auth_prefix", | |
| 1596 | − "entityType": "columns", | |
| 1597 | − "schema": "public", | |
| 1598 | − "table": "sources" | |
| 1599 | − }, | |
| 1600 | − { | |
| 1601 | − "type": "text", | |
| 1602 | − "typeSchema": null, | |
| 1603 | − "notNull": false, | |
| 1604 | − "dimensions": 0, | |
| 1605 | − "default": null, | |
| 1606 | − "generated": null, | |
| 1607 | − "identity": null, | |
| 1608 | − "name": "source_hash", | |
| 1609 | − "entityType": "columns", | |
| 1610 | − "schema": "public", | |
| 1611 | − "table": "sources" | |
| 1612 | − }, | |
| 1613 | − { | |
| 1614 | − "type": "text", | |
| 1615 | − "typeSchema": null, | |
| 1616 | − "notNull": false, | |
| 1617 | − "dimensions": 0, | |
| 1618 | − "default": null, | |
| 1619 | − "generated": null, | |
| 1620 | − "identity": null, | |
| 1621 | − "name": "last_error", | |
| 1622 | − "entityType": "columns", | |
| 1623 | − "schema": "public", | |
| 1624 | − "table": "sources" | |
| 1625 | − }, | |
| 1626 | − { | |
| 1627 | − "type": "bigint", | |
| 1628 | − "typeSchema": null, | |
| 1629 | − "notNull": true, | |
| 1630 | − "dimensions": 0, | |
| 1631 | − "default": null, | |
| 1632 | − "generated": null, | |
| 1633 | − "identity": null, | |
| 1634 | − "name": "created_at", | |
| 1635 | − "entityType": "columns", | |
| 1636 | − "schema": "public", | |
| 1637 | − "table": "sources" | |
| 1638 | − }, | |
| 1639 | − { | |
| 1640 | − "type": "bigint", | |
| 1641 | − "typeSchema": null, | |
| 1642 | − "notNull": true, | |
| 1643 | − "dimensions": 0, | |
| 1644 | − "default": null, | |
| 1645 | − "generated": null, | |
| 1646 | − "identity": null, | |
| 1647 | − "name": "updated_at", | |
| 1648 | − "entityType": "columns", | |
| 1649 | − "schema": "public", | |
| 1650 | − "table": "sources" | |
| 1651 | − }, | |
| 1652 | − { | |
| 1653 | − "type": "text", | |
| 1654 | − "typeSchema": null, | |
| 1655 | − "notNull": true, | |
| 1656 | − "dimensions": 0, | |
| 1657 | − "default": null, | |
| 1658 | − "generated": null, | |
| 1659 | − "identity": null, | |
| 1660 | − "name": "workspace_id", | |
| 1661 | − "entityType": "columns", | |
| 1662 | − "schema": "public", | |
| 1663 | − "table": "tool_artifact_parameters" | |
| 1664 | − }, | |
| 1665 | − { | |
| 1666 | − "type": "text", | |
| 1667 | − "typeSchema": null, | |
| 1668 | − "notNull": true, | |
| 1669 | − "dimensions": 0, | |
| 1670 | − "default": null, | |
| 1671 | − "generated": null, | |
| 1672 | − "identity": null, | |
| 1673 | − "name": "path", | |
| 1674 | − "entityType": "columns", | |
| 1675 | − "schema": "public", | |
| 1676 | − "table": "tool_artifact_parameters" | |
| 1677 | − }, | |
| 1678 | − { | |
| 1679 | − "type": "bigint", | |
| 1680 | − "typeSchema": null, | |
| 1681 | − "notNull": true, | |
| 1682 | − "dimensions": 0, | |
| 1683 | − "default": null, | |
| 1684 | − "generated": null, | |
| 1685 | − "identity": null, | |
| 1686 | − "name": "position", | |
| 1687 | − "entityType": "columns", | |
| 1688 | − "schema": "public", | |
| 1689 | − "table": "tool_artifact_parameters" | |
| 1690 | − }, | |
| 1691 | − { | |
| 1692 | − "type": "text", | |
| 1693 | − "typeSchema": null, | |
| 1694 | − "notNull": true, | |
| 1695 | − "dimensions": 0, | |
| 1696 | − "default": null, | |
| 1697 | − "generated": null, | |
| 1698 | − "identity": null, | |
| 1699 | − "name": "name", | |
| 1700 | − "entityType": "columns", | |
| 1701 | − "schema": "public", | |
| 1702 | − "table": "tool_artifact_parameters" | |
| 1703 | − }, | |
| 1704 | − { | |
| 1705 | − "type": "text", | |
| 1706 | − "typeSchema": null, | |
| 1707 | − "notNull": true, | |
| 1708 | − "dimensions": 0, | |
| 1709 | − "default": null, | |
| 1710 | − "generated": null, | |
| 1711 | − "identity": null, | |
| 1712 | − "name": "location", | |
| 1713 | − "entityType": "columns", | |
| 1714 | − "schema": "public", | |
| 1715 | − "table": "tool_artifact_parameters" | |
| 1716 | − }, | |
| 1717 | − { | |
| 1718 | − "type": "boolean", | |
| 1719 | − "typeSchema": null, | |
| 1720 | − "notNull": true, | |
| 1721 | − "dimensions": 0, | |
| 1722 | − "default": null, | |
| 1723 | − "generated": null, | |
| 1724 | − "identity": null, | |
| 1725 | − "name": "required", | |
| 1726 | − "entityType": "columns", | |
| 1727 | − "schema": "public", | |
| 1728 | − "table": "tool_artifact_parameters" | |
| 1729 | − }, | |
| 1730 | − { | |
| 1731 | − "type": "text", | |
| 1732 | − "typeSchema": null, | |
| 1733 | − "notNull": true, | |
| 1734 | − "dimensions": 0, | |
| 1735 | − "default": null, | |
| 1736 | − "generated": null, | |
| 1737 | − "identity": null, | |
| 1738 | − "name": "workspace_id", | |
| 1739 | − "entityType": "columns", | |
| 1740 | − "schema": "public", | |
| 1741 | − "table": "tool_artifact_ref_hint_keys" | |
| 1742 | − }, | |
| 1743 | − { | |
| 1744 | − "type": "text", | |
| 1745 | − "typeSchema": null, | |
| 1746 | − "notNull": true, | |
| 1747 | − "dimensions": 0, | |
| 1748 | − "default": null, | |
| 1749 | − "generated": null, | |
| 1750 | − "identity": null, | |
| 1751 | − "name": "path", | |
| 1752 | − "entityType": "columns", | |
| 1753 | − "schema": "public", | |
| 1754 | − "table": "tool_artifact_ref_hint_keys" | |
| 1755 | − }, | |
| 1756 | − { | |
| 1757 | − "type": "bigint", | |
| 1758 | − "typeSchema": null, | |
| 1759 | − "notNull": true, | |
| 1760 | − "dimensions": 0, | |
| 1761 | − "default": null, | |
| 1762 | − "generated": null, | |
| 1763 | − "identity": null, | |
| 1764 | − "name": "position", | |
| 1765 | − "entityType": "columns", | |
| 1766 | − "schema": "public", | |
| 1767 | − "table": "tool_artifact_ref_hint_keys" | |
| 1768 | − }, | |
| 1769 | − { | |
| 1770 | − "type": "text", | |
| 1771 | − "typeSchema": null, | |
| 1772 | − "notNull": true, | |
| 1773 | − "dimensions": 0, | |
| 1774 | − "default": null, | |
| 1775 | − "generated": null, | |
| 1776 | − "identity": null, | |
| 1777 | − "name": "ref_hint_key", | |
| 1778 | − "entityType": "columns", | |
| 1779 | − "schema": "public", | |
| 1780 | − "table": "tool_artifact_ref_hint_keys" | |
| 1781 | − }, | |
| 1782 | − { | |
| 1783 | − "type": "text", | |
| 1784 | − "typeSchema": null, | |
| 1785 | − "notNull": true, | |
| 1786 | − "dimensions": 0, | |
| 1787 | − "default": null, | |
| 1788 | − "generated": null, | |
| 1789 | − "identity": null, | |
| 1790 | − "name": "workspace_id", | |
| 1791 | − "entityType": "columns", | |
| 1792 | − "schema": "public", | |
| 1793 | − "table": "tool_artifact_request_body_content_types" | |
| 1794 | − }, | |
| 1795 | − { | |
| 1796 | − "type": "text", | |
| 1797 | − "typeSchema": null, | |
| 1798 | − "notNull": true, | |
| 1799 | − "dimensions": 0, | |
| 1800 | − "default": null, | |
| 1801 | − "generated": null, | |
| 1802 | − "identity": null, | |
| 1803 | − "name": "path", | |
| 1804 | − "entityType": "columns", | |
| 1805 | − "schema": "public", | |
| 1806 | − "table": "tool_artifact_request_body_content_types" | |
| 1807 | − }, | |
| 1808 | − { | |
| 1809 | − "type": "bigint", | |
| 1810 | − "typeSchema": null, | |
| 1811 | − "notNull": true, | |
| 1812 | − "dimensions": 0, | |
| 1813 | − "default": null, | |
| 1814 | − "generated": null, | |
| 1815 | − "identity": null, | |
| 1816 | − "name": "position", | |
| 1817 | − "entityType": "columns", | |
| 1818 | − "schema": "public", | |
| 1819 | − "table": "tool_artifact_request_body_content_types" | |
| 1820 | − }, | |
| 1821 | − { | |
| 1822 | − "type": "text", | |
| 1823 | − "typeSchema": null, | |
| 1824 | − "notNull": true, | |
| 1825 | − "dimensions": 0, | |
| 1826 | − "default": null, | |
| 1827 | − "generated": null, | |
| 1828 | − "identity": null, | |
| 1829 | − "name": "content_type", | |
| 1830 | − "entityType": "columns", | |
| 1831 | − "schema": "public", | |
| 1832 | − "table": "tool_artifact_request_body_content_types" | |
| 1833 | − }, | |
| 1834 | − { | |
| 1835 | − "type": "text", | |
| 1836 | − "typeSchema": null, | |
| 1837 | − "notNull": true, | |
| 1838 | − "dimensions": 0, | |
| 1839 | − "default": null, | |
| 1840 | − "generated": null, | |
| 1841 | − "identity": null, | |
| 1842 | − "name": "workspace_id", | |
| 1843 | − "entityType": "columns", | |
| 1844 | − "schema": "public", | |
| 1845 | − "table": "tool_artifacts" | |
| 1846 | − }, | |
| 1847 | − { | |
| 1848 | − "type": "text", | |
| 1849 | − "typeSchema": null, | |
| 1850 | − "notNull": true, | |
| 1851 | − "dimensions": 0, | |
| 1852 | − "default": null, | |
| 1853 | − "generated": null, | |
| 1854 | − "identity": null, | |
| 1855 | − "name": "path", | |
| 1856 | − "entityType": "columns", | |
| 1857 | − "schema": "public", | |
| 1858 | − "table": "tool_artifacts" | |
| 1859 | − }, | |
| 1860 | − { | |
| 1861 | − "type": "text", | |
| 1862 | − "typeSchema": null, | |
| 1863 | − "notNull": true, | |
| 1864 | − "dimensions": 0, | |
| 1865 | − "default": null, | |
| 1866 | − "generated": null, | |
| 1867 | − "identity": null, | |
| 1868 | − "name": "tool_id", | |
| 1869 | − "entityType": "columns", | |
| 1870 | − "schema": "public", | |
| 1871 | − "table": "tool_artifacts" | |
| 1872 | − }, | |
| 1873 | − { | |
| 1874 | − "type": "text", | |
| 1875 | − "typeSchema": null, | |
| 1876 | − "notNull": true, | |
| 1877 | − "dimensions": 0, | |
| 1878 | − "default": null, | |
| 1879 | − "generated": null, | |
| 1880 | − "identity": null, | |
| 1881 | − "name": "source_id", | |
| 1882 | − "entityType": "columns", | |
| 1883 | − "schema": "public", | |
| 1884 | − "table": "tool_artifacts" | |
| 1885 | − }, | |
| 1886 | − { | |
| 1887 | − "type": "text", | |
| 1888 | − "typeSchema": null, | |
| 1889 | − "notNull": false, | |
| 1890 | − "dimensions": 0, | |
| 1891 | − "default": null, | |
| 1892 | − "generated": null, | |
| 1893 | − "identity": null, | |
| 1894 | − "name": "title", | |
| 1895 | − "entityType": "columns", | |
| 1896 | − "schema": "public", | |
| 1897 | − "table": "tool_artifacts" | |
| 1898 | − }, | |
| 1899 | − { | |
| 1900 | − "type": "text", | |
| 1901 | − "typeSchema": null, | |
| 1902 | − "notNull": false, | |
| 1903 | − "dimensions": 0, | |
| 1904 | − "default": null, | |
| 1905 | − "generated": null, | |
| 1906 | − "identity": null, | |
| 1907 | − "name": "description", | |
| 1908 | − "entityType": "columns", | |
| 1909 | − "schema": "public", | |
| 1910 | − "table": "tool_artifacts" | |
| 1911 | − }, | |
| 1912 | − { | |
| 1913 | − "type": "text", | |
| 1914 | − "typeSchema": null, | |
| 1915 | − "notNull": true, | |
| 1916 | − "dimensions": 0, | |
| 1917 | − "default": null, | |
| 1918 | − "generated": null, | |
| 1919 | − "identity": null, | |
| 1920 | − "name": "search_namespace", | |
| 1921 | − "entityType": "columns", | |
| 1922 | − "schema": "public", | |
| 1923 | − "table": "tool_artifacts" | |
| 1924 | − }, | |
| 1925 | − { | |
| 1926 | − "type": "text", | |
| 1927 | − "typeSchema": null, | |
| 1928 | − "notNull": true, | |
| 1929 | − "dimensions": 0, | |
| 1930 | − "default": null, | |
| 1931 | − "generated": null, | |
| 1932 | − "identity": null, | |
| 1933 | − "name": "search_text", | |
| 1934 | − "entityType": "columns", | |
| 1935 | − "schema": "public", | |
| 1936 | − "table": "tool_artifacts" | |
| 1937 | − }, | |
| 1938 | − { | |
| 1939 | − "type": "text", | |
| 1940 | − "typeSchema": null, | |
| 1941 | − "notNull": false, | |
| 1942 | − "dimensions": 0, | |
| 1943 | − "default": null, | |
| 1944 | − "generated": null, | |
| 1945 | − "identity": null, | |
| 1946 | − "name": "input_schema_json", | |
| 1947 | − "entityType": "columns", | |
| 1948 | − "schema": "public", | |
| 1949 | − "table": "tool_artifacts" | |
| 1950 | − }, | |
| 1951 | − { | |
| 1952 | − "type": "text", | |
| 1953 | − "typeSchema": null, | |
| 1954 | − "notNull": false, | |
| 1955 | − "dimensions": 0, | |
| 1956 | − "default": null, | |
| 1957 | − "generated": null, | |
| 1958 | − "identity": null, | |
| 1959 | − "name": "output_schema_json", | |
| 1960 | − "entityType": "columns", | |
| 1961 | − "schema": "public", | |
| 1962 | − "table": "tool_artifacts" | |
| 1963 | − }, | |
| 1964 | − { | |
| 1965 | − "type": "text", | |
| 1966 | − "typeSchema": null, | |
| 1967 | − "notNull": true, | |
| 1968 | − "dimensions": 0, | |
| 1969 | − "default": null, | |
| 1970 | − "generated": null, | |
| 1971 | − "identity": null, | |
| 1972 | − "name": "provider_kind", | |
| 1973 | − "entityType": "columns", | |
| 1974 | − "schema": "public", | |
| 1975 | − "table": "tool_artifacts" | |
| 1976 | − }, | |
| 1977 | − { | |
| 1978 | − "type": "text", | |
| 1979 | − "typeSchema": null, | |
| 1980 | − "notNull": false, | |
| 1981 | − "dimensions": 0, | |
| 1982 | − "default": null, | |
| 1983 | − "generated": null, | |
| 1984 | − "identity": null, | |
| 1985 | − "name": "mcp_tool_name", | |
| 1986 | − "entityType": "columns", | |
| 1987 | − "schema": "public", | |
| 1988 | − "table": "tool_artifacts" | |
| 1989 | − }, | |
| 1990 | − { | |
| 1991 | − "type": "text", | |
| 1992 | − "typeSchema": null, | |
| 1993 | − "notNull": false, | |
| 1994 | − "dimensions": 0, | |
| 1995 | − "default": null, | |
| 1996 | − "generated": null, | |
| 1997 | − "identity": null, | |
| 1998 | − "name": "openapi_method", | |
| 1999 | − "entityType": "columns", | |
| 2000 | − "schema": "public", | |
| 2001 | − "table": "tool_artifacts" | |
| 2002 | − }, | |
| 2003 | − { | |
| 2004 | − "type": "text", | |
| 2005 | − "typeSchema": null, | |
| 2006 | − "notNull": false, | |
| 2007 | − "dimensions": 0, | |
| 2008 | − "default": null, | |
| 2009 | − "generated": null, | |
| 2010 | − "identity": null, | |
| 2011 | − "name": "openapi_path_template", | |
| 2012 | − "entityType": "columns", | |
| 2013 | − "schema": "public", | |
| 2014 | − "table": "tool_artifacts" | |
| 2015 | − }, | |
| 2016 | − { | |
| 2017 | − "type": "text", | |
| 2018 | − "typeSchema": null, | |
| 2019 | − "notNull": false, | |
| 2020 | − "dimensions": 0, | |
| 2021 | − "default": null, | |
| 2022 | − "generated": null, | |
| 2023 | − "identity": null, | |
| 2024 | − "name": "openapi_operation_hash", | |
| 2025 | − "entityType": "columns", | |
| 2026 | − "schema": "public", | |
| 2027 | − "table": "tool_artifacts" | |
| 2028 | − }, | |
| 2029 | − { | |
| 2030 | − "type": "boolean", | |
| 2031 | − "typeSchema": null, | |
| 2032 | − "notNull": false, | |
| 2033 | − "dimensions": 0, | |
| 2034 | − "default": null, | |
| 2035 | − "generated": null, | |
| 2036 | − "identity": null, | |
| 2037 | − "name": "openapi_request_body_required", | |
| 2038 | − "entityType": "columns", | |
| 2039 | − "schema": "public", | |
| 2040 | − "table": "tool_artifacts" | |
| 2041 | − }, | |
| 2042 | − { | |
| 2043 | − "type": "bigint", | |
| 2044 | − "typeSchema": null, | |
| 2045 | − "notNull": true, | |
| 2046 | − "dimensions": 0, | |
| 2047 | − "default": null, | |
| 2048 | − "generated": null, | |
| 2049 | − "identity": null, | |
| 2050 | − "name": "created_at", | |
| 2051 | − "entityType": "columns", | |
| 2052 | − "schema": "public", | |
| 2053 | − "table": "tool_artifacts" | |
| 2054 | − }, | |
| 2055 | − { | |
| 2056 | − "type": "bigint", | |
| 2057 | − "typeSchema": null, | |
| 2058 | − "notNull": true, | |
| 2059 | − "dimensions": 0, | |
| 2060 | − "default": null, | |
| 2061 | − "generated": null, | |
| 2062 | − "identity": null, | |
| 2063 | − "name": "updated_at", | |
| 2064 | − "entityType": "columns", | |
| 2065 | − "schema": "public", | |
| 2066 | − "table": "tool_artifacts" | |
| 2067 | − }, | |
| 2068 | − { | |
| 2069 | − "type": "text", | |
| 2070 | − "typeSchema": null, | |
| 2071 | − "notNull": true, | |
| 2072 | − "dimensions": 0, | |
| 2073 | − "default": null, | |
| 2074 | − "generated": null, | |
| 2075 | − "identity": null, | |
| 2076 | − "name": "id", | |
| 2077 | − "entityType": "columns", | |
| 2078 | − "schema": "public", | |
| 2079 | − "table": "workspaces" | |
| 2080 | − }, | |
| 2081 | − { | |
| 2082 | − "type": "text", | |
| 2083 | − "typeSchema": null, | |
| 2084 | − "notNull": true, | |
| 2085 | − "dimensions": 0, | |
| 2086 | − "default": null, | |
| 2087 | − "generated": null, | |
| 2088 | − "identity": null, | |
| 2089 | − "name": "organization_id", | |
| 2090 | − "entityType": "columns", | |
| 2091 | − "schema": "public", | |
| 2092 | − "table": "workspaces" | |
| 2093 | − }, | |
| 2094 | − { | |
| 2095 | − "type": "text", | |
| 2096 | − "typeSchema": null, | |
| 2097 | − "notNull": true, | |
| 2098 | − "dimensions": 0, | |
| 2099 | − "default": null, | |
| 2100 | − "generated": null, | |
| 2101 | − "identity": null, | |
| 2102 | − "name": "name", | |
| 2103 | − "entityType": "columns", | |
| 2104 | − "schema": "public", | |
| 2105 | − "table": "workspaces" | |
| 2106 | − }, | |
| 2107 | − { | |
| 2108 | − "type": "text", | |
| 2109 | − "typeSchema": null, | |
| 2110 | − "notNull": false, | |
| 2111 | − "dimensions": 0, | |
| 2112 | − "default": null, | |
| 2113 | − "generated": null, | |
| 2114 | − "identity": null, | |
| 2115 | − "name": "created_by_account_id", | |
| 2116 | − "entityType": "columns", | |
| 2117 | − "schema": "public", | |
| 2118 | − "table": "workspaces" | |
| 2119 | − }, | |
| 2120 | − { | |
| 2121 | − "type": "bigint", | |
| 2122 | − "typeSchema": null, | |
| 2123 | − "notNull": true, | |
| 2124 | − "dimensions": 0, | |
| 2125 | − "default": null, | |
| 2126 | − "generated": null, | |
| 2127 | − "identity": null, | |
| 2128 | − "name": "created_at", | |
| 2129 | − "entityType": "columns", | |
| 2130 | − "schema": "public", | |
| 2131 | − "table": "workspaces" | |
| 2132 | − }, | |
| 2133 | − { | |
| 2134 | − "type": "bigint", | |
| 2135 | − "typeSchema": null, | |
| 2136 | − "notNull": true, | |
| 2137 | − "dimensions": 0, | |
| 2138 | − "default": null, | |
| 2139 | − "generated": null, | |
| 2140 | − "identity": null, | |
| 2141 | − "name": "updated_at", | |
| 2142 | − "entityType": "columns", | |
| 2143 | − "schema": "public", | |
| 2144 | − "table": "workspaces" | |
| 2145 | − }, | |
| 2146 | − { | |
| 2147 | − "nameExplicit": true, | |
| 2148 | − "columns": [ | |
| 2149 | − { | |
| 2150 | − "value": "provider", | |
| 2151 | − "isExpression": false, | |
| 2152 | − "asc": true, | |
| 2153 | − "nullsFirst": false, | |
| 2154 | − "opclass": null | |
| 2155 | − }, | |
| 2156 | − { | |
| 2157 | − "value": "subject", | |
| 2158 | − "isExpression": false, | |
| 2159 | − "asc": true, | |
| 2160 | − "nullsFirst": false, | |
| 2161 | − "opclass": null | |
| 2162 | − } | |
| 2163 | − ], | |
| 2164 | − "isUnique": true, | |
| 2165 | − "where": null, | |
| 2166 | − "with": "", | |
| 2167 | − "method": "btree", | |
| 2168 | − "concurrently": false, | |
| 2169 | − "name": "accounts_provider_subject_idx", | |
| 2170 | − "entityType": "indexes", | |
| 2171 | − "schema": "public", | |
| 2172 | − "table": "accounts" | |
| 2173 | − }, | |
| 2174 | − { | |
| 2175 | − "nameExplicit": true, | |
| 2176 | − "columns": [ | |
| 2177 | − { | |
| 2178 | − "value": "updated_at", | |
| 2179 | − "isExpression": false, | |
| 2180 | − "asc": true, | |
| 2181 | − "nullsFirst": false, | |
| 2182 | − "opclass": null | |
| 2183 | − }, | |
| 2184 | − { | |
| 2185 | − "value": "id", | |
| 2186 | − "isExpression": false, | |
| 2187 | − "asc": true, | |
| 2188 | − "nullsFirst": false, | |
| 2189 | − "opclass": null | |
| 2190 | − } | |
| 2191 | − ], | |
| 2192 | − "isUnique": false, | |
| 2193 | − "where": null, | |
| 2194 | − "with": "", | |
| 2195 | − "method": "btree", | |
| 2196 | − "concurrently": false, | |
| 2197 | − "name": "accounts_updated_idx", | |
| 2198 | − "entityType": "indexes", | |
| 2199 | − "schema": "public", | |
| 2200 | − "table": "accounts" | |
| 2201 | − }, | |
| 2202 | − { | |
| 2203 | − "nameExplicit": true, | |
| 2204 | − "columns": [ | |
| 2205 | − { | |
| 2206 | − "value": "execution_id", | |
| 2207 | − "isExpression": false, | |
| 2208 | − "asc": true, | |
| 2209 | − "nullsFirst": false, | |
| 2210 | − "opclass": null | |
| 2211 | − }, | |
| 2212 | − { | |
| 2213 | − "value": "updated_at", | |
| 2214 | − "isExpression": false, | |
| 2215 | − "asc": true, | |
| 2216 | − "nullsFirst": false, | |
| 2217 | − "opclass": null | |
| 2218 | − }, | |
| 2219 | − { | |
| 2220 | − "value": "id", | |
| 2221 | − "isExpression": false, | |
| 2222 | − "asc": true, | |
| 2223 | − "nullsFirst": false, | |
| 2224 | − "opclass": null | |
| 2225 | − } | |
| 2226 | − ], | |
| 2227 | − "isUnique": false, | |
| 2228 | − "where": null, | |
| 2229 | − "with": "", | |
| 2230 | − "method": "btree", | |
| 2231 | − "concurrently": false, | |
| 2232 | − "name": "execution_interactions_execution_idx", | |
| 2233 | − "entityType": "indexes", | |
| 2234 | − "schema": "public", | |
| 2235 | − "table": "execution_interactions" | |
| 2236 | − }, | |
| 2237 | − { | |
| 2238 | − "nameExplicit": true, | |
| 2239 | − "columns": [ | |
| 2240 | − { | |
| 2241 | − "value": "workspace_id", | |
| 2242 | − "isExpression": false, | |
| 2243 | − "asc": true, | |
| 2244 | − "nullsFirst": false, | |
| 2245 | − "opclass": null | |
| 2246 | − }, | |
| 2247 | − { | |
| 2248 | − "value": "updated_at", | |
| 2249 | − "isExpression": false, | |
| 2250 | − "asc": true, | |
| 2251 | − "nullsFirst": false, | |
| 2252 | − "opclass": null | |
| 2253 | − }, | |
| 2254 | − { | |
| 2255 | − "value": "id", | |
| 2256 | − "isExpression": false, | |
| 2257 | − "asc": true, | |
| 2258 | − "nullsFirst": false, | |
| 2259 | − "opclass": null | |
| 2260 | − } | |
| 2261 | − ], | |
| 2262 | − "isUnique": false, | |
| 2263 | − "where": null, | |
| 2264 | − "with": "", | |
| 2265 | − "method": "btree", | |
| 2266 | − "concurrently": false, | |
| 2267 | − "name": "executions_workspace_idx", | |
| 2268 | − "entityType": "indexes", | |
| 2269 | − "schema": "public", | |
| 2270 | − "table": "executions" | |
| 2271 | − }, | |
| 2272 | − { | |
| 2273 | − "nameExplicit": true, | |
| 2274 | − "columns": [ | |
| 2275 | − { | |
| 2276 | − "value": "organization_id", | |
| 2277 | − "isExpression": false, | |
| 2278 | − "asc": true, | |
| 2279 | − "nullsFirst": false, | |
| 2280 | − "opclass": null | |
| 2281 | − } | |
| 2282 | − ], | |
| 2283 | − "isUnique": false, | |
| 2284 | − "where": null, | |
| 2285 | − "with": "", | |
| 2286 | − "method": "btree", | |
| 2287 | − "concurrently": false, | |
| 2288 | − "name": "organization_memberships_org_idx", | |
| 2289 | − "entityType": "indexes", | |
| 2290 | − "schema": "public", | |
| 2291 | − "table": "organization_memberships" | |
| 2292 | − }, | |
| 2293 | − { | |
| 2294 | − "nameExplicit": true, | |
| 2295 | − "columns": [ | |
| 2296 | − { | |
| 2297 | − "value": "account_id", | |
| 2298 | − "isExpression": false, | |
| 2299 | − "asc": true, | |
| 2300 | − "nullsFirst": false, | |
| 2301 | − "opclass": null | |
| 2302 | − } | |
| 2303 | − ], | |
| 2304 | − "isUnique": false, | |
| 2305 | − "where": null, | |
| 2306 | − "with": "", | |
| 2307 | − "method": "btree", | |
| 2308 | − "concurrently": false, | |
| 2309 | − "name": "organization_memberships_account_idx", | |
| 2310 | − "entityType": "indexes", | |
| 2311 | − "schema": "public", | |
| 2312 | − "table": "organization_memberships" | |
| 2313 | − }, | |
| 2314 | − { | |
| 2315 | − "nameExplicit": true, | |
| 2316 | − "columns": [ | |
| 2317 | − { | |
| 2318 | − "value": "organization_id", | |
| 2319 | − "isExpression": false, | |
| 2320 | − "asc": true, | |
| 2321 | − "nullsFirst": false, | |
| 2322 | − "opclass": null | |
| 2323 | − }, | |
| 2324 | − { | |
| 2325 | − "value": "account_id", | |
| 2326 | − "isExpression": false, | |
| 2327 | − "asc": true, | |
| 2328 | − "nullsFirst": false, | |
| 2329 | − "opclass": null | |
| 2330 | − } | |
| 2331 | − ], | |
| 2332 | − "isUnique": true, | |
| 2333 | − "where": null, | |
| 2334 | − "with": "", | |
| 2335 | − "method": "btree", | |
| 2336 | − "concurrently": false, | |
| 2337 | − "name": "organization_memberships_org_account_idx", | |
| 2338 | − "entityType": "indexes", | |
| 2339 | − "schema": "public", | |
| 2340 | − "table": "organization_memberships" | |
| 2341 | − }, | |
| 2342 | − { | |
| 2343 | − "nameExplicit": true, | |
| 2344 | − "columns": [ | |
| 2345 | − { | |
| 2346 | − "value": "slug", | |
| 2347 | − "isExpression": false, | |
| 2348 | − "asc": true, | |
| 2349 | − "nullsFirst": false, | |
| 2350 | − "opclass": null | |
| 2351 | − } | |
| 2352 | − ], | |
| 2353 | − "isUnique": true, | |
| 2354 | − "where": null, | |
| 2355 | − "with": "", | |
| 2356 | − "method": "btree", | |
| 2357 | − "concurrently": false, | |
| 2358 | − "name": "organizations_slug_idx", | |
| 2359 | − "entityType": "indexes", | |
| 2360 | − "schema": "public", | |
| 2361 | − "table": "organizations" | |
| 2362 | − }, | |
| 2363 | − { | |
| 2364 | − "nameExplicit": true, | |
| 2365 | − "columns": [ | |
| 2366 | − { | |
| 2367 | − "value": "updated_at", | |
| 2368 | − "isExpression": false, | |
| 2369 | − "asc": true, | |
| 2370 | − "nullsFirst": false, | |
| 2371 | − "opclass": null | |
| 2372 | − }, | |
| 2373 | − { | |
| 2374 | − "value": "id", | |
| 2375 | − "isExpression": false, | |
| 2376 | − "asc": true, | |
| 2377 | − "nullsFirst": false, | |
| 2378 | − "opclass": null | |
| 2379 | − } | |
| 2380 | − ], | |
| 2381 | − "isUnique": false, | |
| 2382 | − "where": null, | |
| 2383 | − "with": "", | |
| 2384 | − "method": "btree", | |
| 2385 | − "concurrently": false, | |
| 2386 | − "name": "organizations_updated_idx", | |
| 2387 | − "entityType": "indexes", | |
| 2388 | − "schema": "public", | |
| 2389 | − "table": "organizations" | |
| 2390 | − }, | |
| 2391 | − { | |
| 2392 | − "nameExplicit": true, | |
| 2393 | − "columns": [ | |
| 2394 | − { | |
| 2395 | − "value": "workspace_id", | |
| 2396 | − "isExpression": false, | |
| 2397 | − "asc": true, | |
| 2398 | − "nullsFirst": false, | |
| 2399 | − "opclass": null | |
| 2400 | − }, | |
| 2401 | − { | |
| 2402 | − "value": "updated_at", | |
| 2403 | − "isExpression": false, | |
| 2404 | − "asc": true, | |
| 2405 | − "nullsFirst": false, | |
| 2406 | − "opclass": null | |
| 2407 | − }, | |
| 2408 | − { | |
| 2409 | − "value": "id", | |
| 2410 | − "isExpression": false, | |
| 2411 | − "asc": true, | |
| 2412 | − "nullsFirst": false, | |
| 2413 | − "opclass": null | |
| 2414 | − } | |
| 2415 | − ], | |
| 2416 | − "isUnique": false, | |
| 2417 | − "where": null, | |
| 2418 | − "with": "", | |
| 2419 | − "method": "btree", | |
| 2420 | − "concurrently": false, | |
| 2421 | − "name": "policies_workspace_idx", | |
| 2422 | − "entityType": "indexes", | |
| 2423 | − "schema": "public", | |
| 2424 | − "table": "policies" | |
| 2425 | − }, | |
| 2426 | − { | |
| 2427 | − "nameExplicit": true, | |
| 2428 | − "columns": [ | |
| 2429 | − { | |
| 2430 | − "value": "updated_at", | |
| 2431 | − "isExpression": false, | |
| 2432 | − "asc": true, | |
| 2433 | − "nullsFirst": false, | |
| 2434 | − "opclass": null | |
| 2435 | − }, | |
| 2436 | − { | |
| 2437 | − "value": "id", | |
| 2438 | − "isExpression": false, | |
| 2439 | − "asc": true, | |
| 2440 | − "nullsFirst": false, | |
| 2441 | − "opclass": null | |
| 2442 | − } | |
| 2443 | − ], | |
| 2444 | − "isUnique": false, | |
| 2445 | − "where": null, | |
| 2446 | − "with": "", | |
| 2447 | − "method": "btree", | |
| 2448 | − "concurrently": false, | |
| 2449 | − "name": "secret_materials_updated_idx", | |
| 2450 | − "entityType": "indexes", | |
| 2451 | − "schema": "public", | |
| 2452 | − "table": "secret_materials" | |
| 2453 | − }, | |
| 2454 | − { | |
| 2455 | − "nameExplicit": true, | |
| 2456 | − "columns": [ | |
| 2457 | − { | |
| 2458 | − "value": "workspace_id", | |
| 2459 | − "isExpression": false, | |
| 2460 | − "asc": true, | |
| 2461 | − "nullsFirst": false, | |
| 2462 | − "opclass": null | |
| 2463 | − }, | |
| 2464 | − { | |
| 2465 | − "value": "updated_at", | |
| 2466 | − "isExpression": false, | |
| 2467 | − "asc": true, | |
| 2468 | − "nullsFirst": false, | |
| 2469 | − "opclass": null | |
| 2470 | − }, | |
| 2471 | − { | |
| 2472 | − "value": "id", | |
| 2473 | − "isExpression": false, | |
| 2474 | − "asc": true, | |
| 2475 | − "nullsFirst": false, | |
| 2476 | − "opclass": null | |
| 2477 | − } | |
| 2478 | − ], | |
| 2479 | − "isUnique": false, | |
| 2480 | − "where": null, | |
| 2481 | − "with": "", | |
| 2482 | − "method": "btree", | |
| 2483 | − "concurrently": false, | |
| 2484 | − "name": "source_auth_sessions_workspace_idx", | |
| 2485 | − "entityType": "indexes", | |
| 2486 | − "schema": "public", | |
| 2487 | − "table": "source_auth_sessions" | |
| 2488 | − }, | |
| 2489 | − { | |
| 2490 | − "nameExplicit": true, | |
| 2491 | − "columns": [ | |
| 2492 | − { | |
| 2493 | − "value": "state", | |
| 2494 | − "isExpression": false, | |
| 2495 | − "asc": true, | |
| 2496 | − "nullsFirst": false, | |
| 2497 | − "opclass": null | |
| 2498 | − } | |
| 2499 | − ], | |
| 2500 | − "isUnique": true, | |
| 2501 | − "where": null, | |
| 2502 | − "with": "", | |
| 2503 | − "method": "btree", | |
| 2504 | − "concurrently": false, | |
| 2505 | − "name": "source_auth_sessions_state_idx", | |
| 2506 | − "entityType": "indexes", | |
| 2507 | − "schema": "public", | |
| 2508 | − "table": "source_auth_sessions" | |
| 2509 | − }, | |
| 2510 | − { | |
| 2511 | − "nameExplicit": true, | |
| 2512 | − "columns": [ | |
| 2513 | − { | |
| 2514 | − "value": "workspace_id", | |
| 2515 | − "isExpression": false, | |
| 2516 | − "asc": true, | |
| 2517 | − "nullsFirst": false, | |
| 2518 | − "opclass": null | |
| 2519 | − }, | |
| 2520 | − { | |
| 2521 | − "value": "updated_at", | |
| 2522 | − "isExpression": false, | |
| 2523 | − "asc": true, | |
| 2524 | − "nullsFirst": false, | |
| 2525 | − "opclass": null | |
| 2526 | − }, | |
| 2527 | − { | |
| 2528 | − "value": "source_id", | |
| 2529 | − "isExpression": false, | |
| 2530 | − "asc": true, | |
| 2531 | − "nullsFirst": false, | |
| 2532 | − "opclass": null | |
| 2533 | − } | |
| 2534 | − ], | |
| 2535 | − "isUnique": false, | |
| 2536 | − "where": null, | |
| 2537 | − "with": "", | |
| 2538 | − "method": "btree", | |
| 2539 | − "concurrently": false, | |
| 2540 | − "name": "source_credential_bindings_workspace_idx", | |
| 2541 | − "entityType": "indexes", | |
| 2542 | − "schema": "public", | |
| 2543 | − "table": "source_credential_bindings" | |
| 2544 | − }, | |
| 2545 | − { | |
| 2546 | − "nameExplicit": true, | |
| 2547 | − "columns": [ | |
| 2548 | − { | |
| 2549 | − "value": "workspace_id", | |
| 2550 | − "isExpression": false, | |
| 2551 | − "asc": true, | |
| 2552 | − "nullsFirst": false, | |
| 2553 | − "opclass": null | |
| 2554 | − }, | |
| 2555 | − { | |
| 2556 | − "value": "name", | |
| 2557 | − "isExpression": false, | |
| 2558 | − "asc": true, | |
| 2559 | − "nullsFirst": false, | |
| 2560 | − "opclass": null | |
| 2561 | − } | |
| 2562 | − ], | |
| 2563 | − "isUnique": true, | |
| 2564 | − "where": null, | |
| 2565 | − "with": "", | |
| 2566 | − "method": "btree", | |
| 2567 | − "concurrently": false, | |
| 2568 | − "name": "sources_workspace_name_idx", | |
| 2569 | − "entityType": "indexes", | |
| 2570 | − "schema": "public", | |
| 2571 | − "table": "sources" | |
| 2572 | − }, | |
| 2573 | − { | |
| 2574 | − "nameExplicit": true, | |
| 2575 | − "columns": [ | |
| 2576 | − { | |
| 2577 | − "value": "workspace_id", | |
| 2578 | − "isExpression": false, | |
| 2579 | − "asc": true, | |
| 2580 | − "nullsFirst": false, | |
| 2581 | − "opclass": null | |
| 2582 | − }, | |
| 2583 | − { | |
| 2584 | − "value": "path", | |
| 2585 | − "isExpression": false, | |
| 2586 | − "asc": true, | |
| 2587 | − "nullsFirst": false, | |
| 2588 | − "opclass": null | |
| 2589 | − }, | |
| 2590 | − { | |
| 2591 | − "value": "position", | |
| 2592 | − "isExpression": false, | |
| 2593 | − "asc": true, | |
| 2594 | − "nullsFirst": false, | |
| 2595 | − "opclass": null | |
| 2596 | − } | |
| 2597 | − ], | |
| 2598 | − "isUnique": false, | |
| 2599 | − "where": null, | |
| 2600 | − "with": "", | |
| 2601 | − "method": "btree", | |
| 2602 | − "concurrently": false, | |
| 2603 | − "name": "tool_artifact_parameters_lookup_idx", | |
| 2604 | − "entityType": "indexes", | |
| 2605 | − "schema": "public", | |
| 2606 | − "table": "tool_artifact_parameters" | |
| 2607 | − }, | |
| 2608 | − { | |
| 2609 | − "nameExplicit": true, | |
| 2610 | − "columns": [ | |
| 2611 | − { | |
| 2612 | − "value": "workspace_id", | |
| 2613 | − "isExpression": false, | |
| 2614 | − "asc": true, | |
| 2615 | − "nullsFirst": false, | |
| 2616 | − "opclass": null | |
| 2617 | − }, | |
| 2618 | − { | |
| 2619 | − "value": "path", | |
| 2620 | − "isExpression": false, | |
| 2621 | − "asc": true, | |
| 2622 | − "nullsFirst": false, | |
| 2623 | − "opclass": null | |
| 2624 | − }, | |
| 2625 | − { | |
| 2626 | − "value": "position", | |
| 2627 | − "isExpression": false, | |
| 2628 | − "asc": true, | |
| 2629 | − "nullsFirst": false, | |
| 2630 | − "opclass": null | |
| 2631 | − } | |
| 2632 | − ], | |
| 2633 | − "isUnique": false, | |
| 2634 | − "where": null, | |
| 2635 | − "with": "", | |
| 2636 | − "method": "btree", | |
| 2637 | − "concurrently": false, | |
| 2638 | − "name": "tool_artifact_ref_hint_keys_lookup_idx", | |
| 2639 | − "entityType": "indexes", | |
| 2640 | − "schema": "public", | |
| 2641 | − "table": "tool_artifact_ref_hint_keys" | |
| 2642 | − }, | |
| 2643 | − { | |
| 2644 | − "nameExplicit": true, | |
| 2645 | − "columns": [ | |
| 2646 | − { | |
| 2647 | − "value": "workspace_id", | |
| 2648 | − "isExpression": false, | |
| 2649 | − "asc": true, | |
| 2650 | − "nullsFirst": false, | |
| 2651 | − "opclass": null | |
| 2652 | − }, | |
| 2653 | − { | |
| 2654 | − "value": "path", | |
| 2655 | − "isExpression": false, | |
| 2656 | − "asc": true, | |
| 2657 | − "nullsFirst": false, | |
| 2658 | − "opclass": null | |
| 2659 | − }, | |
| 2660 | − { | |
| 2661 | − "value": "position", | |
| 2662 | − "isExpression": false, | |
| 2663 | − "asc": true, | |
| 2664 | − "nullsFirst": false, | |
| 2665 | − "opclass": null | |
| 2666 | − } | |
| 2667 | − ], | |
| 2668 | − "isUnique": false, | |
| 2669 | − "where": null, | |
| 2670 | − "with": "", | |
| 2671 | − "method": "btree", | |
| 2672 | − "concurrently": false, | |
| 2673 | − "name": "tool_artifact_request_body_content_types_lookup_idx", | |
| 2674 | − "entityType": "indexes", | |
| 2675 | − "schema": "public", | |
| 2676 | − "table": "tool_artifact_request_body_content_types" | |
| 2677 | − }, | |
| 2678 | − { | |
| 2679 | − "nameExplicit": true, | |
| 2680 | − "columns": [ | |
| 2681 | − { | |
| 2682 | − "value": "workspace_id", | |
| 2683 | − "isExpression": false, | |
| 2684 | − "asc": true, | |
| 2685 | − "nullsFirst": false, | |
| 2686 | − "opclass": null | |
| 2687 | − }, | |
| 2688 | − { | |
| 2689 | − "value": "source_id", | |
| 2690 | − "isExpression": false, | |
| 2691 | − "asc": true, | |
| 2692 | − "nullsFirst": false, | |
| 2693 | − "opclass": null | |
| 2694 | − }, | |
| 2695 | − { | |
| 2696 | − "value": "updated_at", | |
| 2697 | − "isExpression": false, | |
| 2698 | − "asc": true, | |
| 2699 | − "nullsFirst": false, | |
| 2700 | − "opclass": null | |
| 2701 | − }, | |
| 2702 | − { | |
| 2703 | − "value": "path", | |
| 2704 | − "isExpression": false, | |
| 2705 | − "asc": true, | |
| 2706 | − "nullsFirst": false, | |
| 2707 | − "opclass": null | |
| 2708 | − } | |
| 2709 | − ], | |
| 2710 | − "isUnique": false, | |
| 2711 | − "where": null, | |
| 2712 | − "with": "", | |
| 2713 | − "method": "btree", | |
| 2714 | − "concurrently": false, | |
| 2715 | − "name": "tool_artifacts_workspace_source_idx", | |
| 2716 | − "entityType": "indexes", | |
| 2717 | − "schema": "public", | |
| 2718 | − "table": "tool_artifacts" | |
| 2719 | − }, | |
| 2720 | − { | |
| 2721 | − "nameExplicit": true, | |
| 2722 | − "columns": [ | |
| 2723 | − { | |
| 2724 | − "value": "workspace_id", | |
| 2725 | − "isExpression": false, | |
| 2726 | − "asc": true, | |
| 2727 | − "nullsFirst": false, | |
| 2728 | − "opclass": null | |
| 2729 | − }, | |
| 2730 | − { | |
| 2731 | − "value": "search_namespace", | |
| 2732 | − "isExpression": false, | |
| 2733 | − "asc": true, | |
| 2734 | − "nullsFirst": false, | |
| 2735 | − "opclass": null | |
| 2736 | − }, | |
| 2737 | − { | |
| 2738 | − "value": "path", | |
| 2739 | − "isExpression": false, | |
| 2740 | − "asc": true, | |
| 2741 | − "nullsFirst": false, | |
| 2742 | − "opclass": null | |
| 2743 | − } | |
| 2744 | − ], | |
| 2745 | − "isUnique": false, | |
| 2746 | − "where": null, | |
| 2747 | − "with": "", | |
| 2748 | − "method": "btree", | |
| 2749 | − "concurrently": false, | |
| 2750 | − "name": "tool_artifacts_workspace_namespace_idx", | |
| 2751 | − "entityType": "indexes", | |
| 2752 | − "schema": "public", | |
| 2753 | − "table": "tool_artifacts" | |
| 2754 | − }, | |
| 2755 | − { | |
| 2756 | − "nameExplicit": true, | |
| 2757 | − "columns": [ | |
| 2758 | − { | |
| 2759 | − "value": "organization_id", | |
| 2760 | − "isExpression": false, | |
| 2761 | − "asc": true, | |
| 2762 | − "nullsFirst": false, | |
| 2763 | − "opclass": null | |
| 2764 | − } | |
| 2765 | − ], | |
| 2766 | − "isUnique": false, | |
| 2767 | − "where": null, | |
| 2768 | − "with": "", | |
| 2769 | − "method": "btree", | |
| 2770 | − "concurrently": false, | |
| 2771 | − "name": "workspaces_org_idx", | |
| 2772 | − "entityType": "indexes", | |
| 2773 | − "schema": "public", | |
| 2774 | − "table": "workspaces" | |
| 2775 | − }, | |
| 2776 | − { | |
| 2777 | − "nameExplicit": true, | |
| 2778 | − "columns": [ | |
| 2779 | − { | |
| 2780 | − "value": "organization_id", | |
| 2781 | − "isExpression": false, | |
| 2782 | − "asc": true, | |
| 2783 | − "nullsFirst": false, | |
| 2784 | − "opclass": null | |
| 2785 | − }, | |
| 2786 | − { | |
| 2787 | − "value": "name", | |
| 2788 | − "isExpression": false, | |
| 2789 | − "asc": true, | |
| 2790 | − "nullsFirst": false, | |
| 2791 | − "opclass": null | |
| 2792 | − } | |
| 2793 | − ], | |
| 2794 | − "isUnique": true, | |
| 2795 | − "where": null, | |
| 2796 | − "with": "", | |
| 2797 | − "method": "btree", | |
| 2798 | − "concurrently": false, | |
| 2799 | − "name": "workspaces_org_name_idx", | |
| 2800 | − "entityType": "indexes", | |
| 2801 | − "schema": "public", | |
| 2802 | − "table": "workspaces" | |
| 2803 | − }, | |
| 2804 | − { | |
| 2805 | − "columns": [ | |
| 2806 | − "workspace_id", | |
| 2807 | − "source_id" | |
| 2808 | − ], | |
| 2809 | − "nameExplicit": false, | |
| 2810 | − "name": "source_credential_bindings_pkey", | |
| 2811 | − "entityType": "pks", | |
| 2812 | − "schema": "public", | |
| 2813 | − "table": "source_credential_bindings" | |
| 2814 | − }, | |
| 2815 | − { | |
| 2816 | − "columns": [ | |
| 2817 | − "workspace_id", | |
| 2818 | − "source_id" | |
| 2819 | − ], | |
| 2820 | − "nameExplicit": false, | |
| 2821 | − "name": "sources_pkey", | |
| 2822 | − "entityType": "pks", | |
| 2823 | − "schema": "public", | |
| 2824 | − "table": "sources" | |
| 2825 | − }, | |
| 2826 | − { | |
| 2827 | − "columns": [ | |
| 2828 | − "workspace_id", | |
| 2829 | − "path", | |
| 2830 | − "position" | |
| 2831 | − ], | |
| 2832 | − "nameExplicit": false, | |
| 2833 | − "name": "tool_artifact_parameters_pkey", | |
| 2834 | − "entityType": "pks", | |
| 2835 | − "schema": "public", | |
| 2836 | − "table": "tool_artifact_parameters" | |
| 2837 | − }, | |
| 2838 | − { | |
| 2839 | − "columns": [ | |
| 2840 | − "workspace_id", | |
| 2841 | − "path", | |
| 2842 | − "position" | |
| 2843 | − ], | |
| 2844 | − "nameExplicit": false, | |
| 2845 | − "name": "tool_artifact_ref_hint_keys_pkey", | |
| 2846 | − "entityType": "pks", | |
| 2847 | − "schema": "public", | |
| 2848 | − "table": "tool_artifact_ref_hint_keys" | |
| 2849 | − }, | |
| 2850 | − { | |
| 2851 | − "columns": [ | |
| 2852 | − "workspace_id", | |
| 2853 | − "path", | |
| 2854 | − "position" | |
| 2855 | − ], | |
| 2856 | − "nameExplicit": false, | |
| 2857 | − "name": "tool_artifact_request_body_content_types_pkey", | |
| 2858 | − "entityType": "pks", | |
| 2859 | − "schema": "public", | |
| 2860 | − "table": "tool_artifact_request_body_content_types" | |
| 2861 | − }, | |
| 2862 | − { | |
| 2863 | − "columns": [ | |
| 2864 | − "workspace_id", | |
| 2865 | − "path" | |
| 2866 | − ], | |
| 2867 | − "nameExplicit": false, | |
| 2868 | − "name": "tool_artifacts_pkey", | |
| 2869 | − "entityType": "pks", | |
| 2870 | − "schema": "public", | |
| 2871 | − "table": "tool_artifacts" | |
| 2872 | − }, | |
| 2873 | − { | |
| 2874 | − "columns": [ | |
| 2875 | − "id" | |
| 2876 | − ], | |
| 2877 | − "nameExplicit": false, | |
| 2878 | − "name": "accounts_pkey", | |
| 2879 | − "schema": "public", | |
| 2880 | − "table": "accounts", | |
| 2881 | − "entityType": "pks" | |
| 2882 | − }, | |
| 2883 | − { | |
| 2884 | − "columns": [ | |
| 2885 | − "id" | |
| 2886 | − ], | |
| 2887 | − "nameExplicit": false, | |
| 2888 | − "name": "execution_interactions_pkey", | |
| 2889 | − "schema": "public", | |
| 2890 | − "table": "execution_interactions", | |
| 2891 | − "entityType": "pks" | |
| 2892 | − }, | |
| 2893 | − { | |
| 2894 | − "columns": [ | |
| 2895 | − "id" | |
| 2896 | − ], | |
| 2897 | − "nameExplicit": false, | |
| 2898 | − "name": "executions_pkey", | |
| 2899 | − "schema": "public", | |
| 2900 | − "table": "executions", | |
| 2901 | − "entityType": "pks" | |
| 2902 | − }, | |
| 2903 | − { | |
| 2904 | − "columns": [ | |
| 2905 | − "id" | |
| 2906 | − ], | |
| 2907 | − "nameExplicit": false, | |
| 2908 | − "name": "local_installations_pkey", | |
| 2909 | − "schema": "public", | |
| 2910 | − "table": "local_installations", | |
| 2911 | − "entityType": "pks" | |
| 2912 | − }, | |
| 2913 | − { | |
| 2914 | − "columns": [ | |
| 2915 | − "id" | |
| 2916 | − ], | |
| 2917 | − "nameExplicit": false, | |
| 2918 | − "name": "organization_memberships_pkey", | |
| 2919 | − "schema": "public", | |
| 2920 | − "table": "organization_memberships", | |
| 2921 | − "entityType": "pks" | |
| 2922 | − }, | |
| 2923 | − { | |
| 2924 | − "columns": [ | |
| 2925 | − "id" | |
| 2926 | − ], | |
| 2927 | − "nameExplicit": false, | |
| 2928 | − "name": "organizations_pkey", | |
| 2929 | − "schema": "public", | |
| 2930 | − "table": "organizations", | |
| 2931 | − "entityType": "pks" | |
| 2932 | − }, | |
| 2933 | − { | |
| 2934 | − "columns": [ | |
| 2935 | − "id" | |
| 2936 | − ], | |
| 2937 | − "nameExplicit": false, | |
| 2938 | − "name": "policies_pkey", | |
| 2939 | − "schema": "public", | |
| 2940 | − "table": "policies", | |
| 2941 | − "entityType": "pks" | |
| 2942 | − }, | |
| 2943 | − { | |
| 2944 | − "columns": [ | |
| 2945 | − "id" | |
| 2946 | − ], | |
| 2947 | − "nameExplicit": false, | |
| 2948 | − "name": "secret_materials_pkey", | |
| 2949 | − "schema": "public", | |
| 2950 | − "table": "secret_materials", | |
| 2951 | − "entityType": "pks" | |
| 2952 | − }, | |
| 2953 | − { | |
| 2954 | − "columns": [ | |
| 2955 | − "id" | |
| 2956 | − ], | |
| 2957 | − "nameExplicit": false, | |
| 2958 | − "name": "source_auth_sessions_pkey", | |
| 2959 | − "schema": "public", | |
| 2960 | − "table": "source_auth_sessions", | |
| 2961 | − "entityType": "pks" | |
| 2962 | − }, | |
| 2963 | − { | |
| 2964 | − "columns": [ | |
| 2965 | − "id" | |
| 2966 | − ], | |
| 2967 | − "nameExplicit": false, | |
| 2968 | − "name": "workspaces_pkey", | |
| 2969 | − "schema": "public", | |
| 2970 | − "table": "workspaces", | |
| 2971 | − "entityType": "pks" | |
| 2972 | − }, | |
| 2973 | − { | |
| 2974 | − "value": "\"provider\" in ('local', 'workos', 'service')", | |
| 2975 | − "name": "accounts_provider_check", | |
| 2976 | − "entityType": "checks", | |
| 2977 | − "schema": "public", | |
| 2978 | − "table": "accounts" | |
| 2979 | − }, | |
| 2980 | − { | |
| 2981 | − "value": "\"status\" in ('pending', 'resolved', 'cancelled')", | |
| 2982 | − "name": "execution_interactions_status_check", | |
| 2983 | − "entityType": "checks", | |
| 2984 | − "schema": "public", | |
| 2985 | − "table": "execution_interactions" | |
| 2986 | − }, | |
| 2987 | − { | |
| 2988 | − "value": "\"status\" in ('pending', 'running', 'waiting_for_interaction', 'completed', 'failed', 'cancelled')", | |
| 2989 | − "name": "executions_status_check", | |
| 2990 | − "entityType": "checks", | |
| 2991 | − "schema": "public", | |
| 2992 | − "table": "executions" | |
| 2993 | − }, | |
| 2994 | − { | |
| 2995 | − "value": "\"role\" in ('viewer', 'editor', 'admin', 'owner')", | |
| 2996 | − "name": "organization_memberships_role_check", | |
| 2997 | − "entityType": "checks", | |
| 2998 | − "schema": "public", | |
| 2999 | − "table": "organization_memberships" | |
| 3000 | − }, | |
| 3001 | − { | |
| 3002 | − "value": "\"status\" in ('invited', 'active', 'suspended', 'removed')", | |
| 3003 | − "name": "organization_memberships_status_check", | |
| 3004 | − "entityType": "checks", | |
| 3005 | − "schema": "public", | |
| 3006 | − "table": "organization_memberships" | |
| 3007 | − }, | |
| 3008 | − { | |
| 3009 | − "value": "\"status\" in ('active', 'suspended', 'archived')", | |
| 3010 | − "name": "organizations_status_check", | |
| 3011 | − "entityType": "checks", | |
| 3012 | − "schema": "public", | |
| 3013 | − "table": "organizations" | |
| 3014 | − }, | |
| 3015 | − { | |
| 3016 | − "value": "\"resource_type\" in ('all_tools', 'source', 'namespace', 'tool_path')", | |
| 3017 | − "name": "policies_resource_type_check", | |
| 3018 | − "entityType": "checks", | |
| 3019 | − "schema": "public", | |
| 3020 | − "table": "policies" | |
| 3021 | − }, | |
| 3022 | − { | |
| 3023 | − "value": "\"match_type\" in ('glob', 'exact')", | |
| 3024 | − "name": "policies_match_type_check", | |
| 3025 | − "entityType": "checks", | |
| 3026 | − "schema": "public", | |
| 3027 | − "table": "policies" | |
| 3028 | − }, | |
| 3029 | − { | |
| 3030 | − "value": "\"effect\" in ('allow', 'deny')", | |
| 3031 | − "name": "policies_effect_check", | |
| 3032 | − "entityType": "checks", | |
| 3033 | − "schema": "public", | |
| 3034 | − "table": "policies" | |
| 3035 | − }, | |
| 3036 | − { | |
| 3037 | − "value": "\"approval_mode\" in ('auto', 'required')", | |
| 3038 | − "name": "policies_approval_mode_check", | |
| 3039 | − "entityType": "checks", | |
| 3040 | − "schema": "public", | |
| 3041 | − "table": "policies" | |
| 3042 | − }, | |
| 3043 | − { | |
| 3044 | − "value": "\"purpose\" in ('auth_material', 'oauth_access_token', 'oauth_refresh_token', 'oauth_client_info')", | |
| 3045 | − "name": "secret_materials_purpose_check", | |
| 3046 | − "entityType": "checks", | |
| 3047 | − "schema": "public", | |
| 3048 | − "table": "secret_materials" | |
| 3049 | − }, | |
| 3050 | − { | |
| 3051 | − "value": "\"strategy\" in ('oauth2_authorization_code')", | |
| 3052 | − "name": "source_auth_sessions_strategy_check", | |
| 3053 | − "entityType": "checks", | |
| 3054 | − "schema": "public", | |
| 3055 | − "table": "source_auth_sessions" | |
| 3056 | − }, | |
| 3057 | − { | |
| 3058 | − "value": "\"status\" in ('pending', 'completed', 'failed', 'cancelled')", | |
| 3059 | − "name": "source_auth_sessions_status_check", | |
| 3060 | − "entityType": "checks", | |
| 3061 | − "schema": "public", | |
| 3062 | − "table": "source_auth_sessions" | |
| 3063 | − }, | |
| 3064 | − { | |
| 3065 | − "value": "\"kind\" in ('mcp', 'openapi', 'graphql', 'internal')", | |
| 3066 | − "name": "sources_kind_check", | |
| 3067 | − "entityType": "checks", | |
| 3068 | − "schema": "public", | |
| 3069 | − "table": "sources" | |
| 3070 | − }, | |
| 3071 | − { | |
| 3072 | − "value": "\"status\" in ('draft', 'probing', 'auth_required', 'connected', 'error')", | |
| 3073 | − "name": "sources_status_check", | |
| 3074 | − "entityType": "checks", | |
| 3075 | − "schema": "public", | |
| 3076 | − "table": "sources" | |
| 3077 | − }, | |
| 3078 | − { | |
| 3079 | − "value": "\"transport\" is null or \"transport\" in ('auto', 'streamable-http', 'sse')", | |
| 3080 | − "name": "sources_transport_check", | |
| 3081 | − "entityType": "checks", | |
| 3082 | − "schema": "public", | |
| 3083 | − "table": "sources" | |
| 3084 | − }, | |
| 3085 | − { | |
| 3086 | − "value": "\"auth_kind\" in ('none', 'bearer', 'oauth2')", | |
| 3087 | − "name": "sources_auth_kind_check", | |
| 3088 | − "entityType": "checks", | |
| 3089 | − "schema": "public", | |
| 3090 | − "table": "sources" | |
| 3091 | − }, | |
| 3092 | − { | |
| 3093 | − "value": "\"location\" in ('path', 'query', 'header', 'cookie')", | |
| 3094 | − "name": "tool_artifact_parameters_location_check", | |
| 3095 | − "entityType": "checks", | |
| 3096 | − "schema": "public", | |
| 3097 | − "table": "tool_artifact_parameters" | |
| 3098 | − }, | |
| 3099 | − { | |
| 3100 | − "value": "\"provider_kind\" in ('mcp', 'openapi')", | |
| 3101 | − "name": "tool_artifacts_provider_kind_check", | |
| 3102 | − "entityType": "checks", | |
| 3103 | − "schema": "public", | |
| 3104 | − "table": "tool_artifacts" | |
| 3105 | − }, | |
| 3106 | − { | |
| 3107 | − "value": "\"provider_kind\" <> 'mcp'\n or (\n \"mcp_tool_name\" is not null\n and \"openapi_method\" is null\n and \"openapi_path_template\" is null\n and \"openapi_operation_hash\" is null\n and \"openapi_request_body_required\" is null\n )", | |
| 3108 | − "name": "tool_artifacts_mcp_shape_check", | |
| 3109 | − "entityType": "checks", | |
| 3110 | − "schema": "public", | |
| 3111 | − "table": "tool_artifacts" | |
| 3112 | − }, | |
| 3113 | − { | |
| 3114 | − "value": "\"provider_kind\" <> 'openapi'\n or (\n \"mcp_tool_name\" is null\n and \"openapi_method\" in ('get', 'put', 'post', 'delete', 'patch', 'head', 'options', 'trace')\n and \"openapi_path_template\" is not null\n and \"openapi_operation_hash\" is not null\n )", | |
| 3115 | − "name": "tool_artifacts_openapi_shape_check", | |
| 3116 | − "entityType": "checks", | |
| 3117 | − "schema": "public", | |
| 3118 | − "table": "tool_artifacts" | |
| 3119 | − } | |
| 3120 | − ], | |
| 3121 | − "renames": [] | |
| 3122 | −} | |
| @@ -1,37 +0,0 @@ | ||
| 1 | −ALTER TABLE "tool_artifacts" ADD COLUMN "openapi_raw_tool_id" text;--> statement-breakpoint | |
| 2 | −ALTER TABLE "tool_artifacts" ADD COLUMN "openapi_operation_id" text;--> statement-breakpoint | |
| 3 | −ALTER TABLE "tool_artifacts" ADD COLUMN "openapi_tags_json" text;--> statement-breakpoint | |
| 4 | −UPDATE "tool_artifacts" | |
| 5 | −SET | |
| 6 | − "openapi_raw_tool_id" = "tool_id", | |
| 7 | − "openapi_operation_id" = "tool_id", | |
| 8 | − "openapi_tags_json" = '[]' | |
| 9 | −WHERE "provider_kind" = 'openapi';--> statement-breakpoint | |
| 10 | −ALTER TABLE "tool_artifacts" DROP CONSTRAINT "tool_artifacts_mcp_shape_check";--> statement-breakpoint | |
| 11 | −ALTER TABLE "tool_artifacts" DROP CONSTRAINT "tool_artifacts_openapi_shape_check";--> statement-breakpoint | |
| 12 | −ALTER TABLE "tool_artifacts" | |
| 13 | −ADD CONSTRAINT "tool_artifacts_mcp_shape_check" CHECK ( | |
| 14 | − "provider_kind" <> 'mcp' | |
| 15 | − OR ( | |
| 16 | − "mcp_tool_name" is not null | |
| 17 | − and "openapi_method" is null | |
| 18 | − and "openapi_path_template" is null | |
| 19 | − and "openapi_operation_hash" is null | |
| 20 | − and "openapi_raw_tool_id" is null | |
| 21 | − and "openapi_operation_id" is null | |
| 22 | − and "openapi_tags_json" is null | |
| 23 | − and "openapi_request_body_required" is null | |
| 24 | − ) | |
| 25 | −);--> statement-breakpoint | |
| 26 | −ALTER TABLE "tool_artifacts" | |
| 27 | −ADD CONSTRAINT "tool_artifacts_openapi_shape_check" CHECK ( | |
| 28 | − "provider_kind" <> 'openapi' | |
| 29 | − OR ( | |
| 30 | − "mcp_tool_name" is null | |
| 31 | − and "openapi_method" in ('get', 'put', 'post', 'delete', 'patch', 'head', 'options', 'trace') | |
| 32 | − and "openapi_path_template" is not null | |
| 33 | − and "openapi_operation_hash" is not null | |
| 34 | − and "openapi_raw_tool_id" is not null | |
| 35 | − and "openapi_tags_json" is not null | |
| 36 | − ) | |
| 37 | −); | |
| @@ -1 +0,0 @@ | ||
| 1 | −ALTER TABLE "sources" ADD COLUMN "source_document_text" text; | |
| @@ -1,41 +0,0 @@ | ||
| 1 | −CREATE TABLE "credentials" ( | |
| 2 | − "id" text PRIMARY KEY NOT NULL, | |
| 3 | − "workspace_id" text NOT NULL, | |
| 4 | − "auth_kind" text NOT NULL, | |
| 5 | − "auth_header_name" text NOT NULL, | |
| 6 | − "auth_prefix" text NOT NULL, | |
| 7 | − "token_provider_id" text NOT NULL, | |
| 8 | − "token_handle" text NOT NULL, | |
| 9 | − "refresh_token_provider_id" text, | |
| 10 | − "refresh_token_handle" text, | |
| 11 | − "created_at" bigint NOT NULL, | |
| 12 | − "updated_at" bigint NOT NULL, | |
| 13 | − CONSTRAINT "credentials_auth_kind_check" CHECK ("auth_kind" in ('bearer', 'oauth2')) | |
| 14 | −); | |
| 15 | −--> statement-breakpoint | |
| 16 | −CREATE INDEX "credentials_workspace_idx" ON "credentials" ("workspace_id","updated_at","id"); | |
| 17 | −--> statement-breakpoint | |
| 18 | −ALTER TABLE "sources" DROP CONSTRAINT "sources_auth_kind_check"; | |
| 19 | −--> statement-breakpoint | |
| 20 | −ALTER TABLE "sources" DROP COLUMN "auth_kind"; | |
| 21 | −--> statement-breakpoint | |
| 22 | −ALTER TABLE "sources" DROP COLUMN "auth_header_name"; | |
| 23 | −--> statement-breakpoint | |
| 24 | −ALTER TABLE "sources" DROP COLUMN "auth_prefix"; | |
| 25 | −--> statement-breakpoint | |
| 26 | −DROP INDEX IF EXISTS "source_credential_bindings_workspace_idx"; | |
| 27 | −--> statement-breakpoint | |
| 28 | −DROP TABLE "source_credential_bindings"; | |
| 29 | −--> statement-breakpoint | |
| 30 | −CREATE TABLE "source_credential_bindings" ( | |
| 31 | − "id" text PRIMARY KEY NOT NULL, | |
| 32 | − "workspace_id" text NOT NULL, | |
| 33 | − "source_id" text NOT NULL, | |
| 34 | − "credential_id" text NOT NULL, | |
| 35 | − "created_at" bigint NOT NULL, | |
| 36 | − "updated_at" bigint NOT NULL | |
| 37 | −); | |
| 38 | −--> statement-breakpoint | |
| 39 | −CREATE UNIQUE INDEX "source_credential_bindings_workspace_source_idx" ON "source_credential_bindings" ("workspace_id","source_id"); | |
| 40 | −--> statement-breakpoint | |
| 41 | −CREATE INDEX "source_credential_bindings_workspace_idx" ON "source_credential_bindings" ("workspace_id","updated_at","source_id"); | |
| @@ -1,16 +0,0 @@ | ||
| 1 | −DROP INDEX IF EXISTS "organization_memberships_org_idx";--> statement-breakpoint | |
| 2 | −DROP INDEX IF EXISTS "organization_memberships_account_idx";--> statement-breakpoint | |
| 3 | −CREATE INDEX "organization_memberships_org_updated_idx" ON "organization_memberships" ("organization_id","updated_at","id");--> statement-breakpoint | |
| 4 | −CREATE INDEX "organization_memberships_account_updated_idx" ON "organization_memberships" ("account_id","updated_at","id");--> statement-breakpoint | |
| 5 | −DROP INDEX IF EXISTS "workspaces_org_idx";--> statement-breakpoint | |
| 6 | −CREATE INDEX "workspaces_org_updated_idx" ON "workspaces" ("organization_id","updated_at","id");--> statement-breakpoint | |
| 7 | −CREATE INDEX "sources_workspace_updated_idx" ON "sources" ("workspace_id","updated_at","source_id");--> statement-breakpoint | |
| 8 | −CREATE INDEX "tool_artifacts_search_text_idx" ON "tool_artifacts" USING gin (to_tsvector('simple', "search_text"));--> statement-breakpoint | |
| 9 | −DROP INDEX IF EXISTS "tool_artifact_parameters_lookup_idx";--> statement-breakpoint | |
| 10 | −DROP INDEX IF EXISTS "tool_artifact_request_body_content_types_lookup_idx";--> statement-breakpoint | |
| 11 | −DROP INDEX IF EXISTS "tool_artifact_ref_hint_keys_lookup_idx";--> statement-breakpoint | |
| 12 | −CREATE INDEX "source_auth_sessions_pending_idx" ON "source_auth_sessions" ("workspace_id","source_id","status","updated_at","id");--> statement-breakpoint | |
| 13 | −DROP INDEX IF EXISTS "policies_workspace_idx";--> statement-breakpoint | |
| 14 | −CREATE INDEX "policies_workspace_priority_idx" ON "policies" ("workspace_id","priority" DESC,"updated_at","id");--> statement-breakpoint | |
| 15 | −CREATE INDEX "local_installations_organization_idx" ON "local_installations" ("organization_id");--> statement-breakpoint | |
| 16 | −CREATE INDEX "local_installations_workspace_idx" ON "local_installations" ("workspace_id"); | |
| @@ -1 +0,0 @@ | ||
| 1 | −ALTER TABLE "secret_materials" ADD COLUMN "name" text; | |
| @@ -1,17 +0,0 @@ | ||
| 1 | −ALTER TABLE "policies" ADD COLUMN "scope_type" text;--> statement-breakpoint | |
| 2 | −ALTER TABLE "policies" ADD COLUMN "organization_id" text;--> statement-breakpoint | |
| 3 | −UPDATE "policies" AS "pol" | |
| 4 | −SET | |
| 5 | − "scope_type" = 'workspace', | |
| 6 | − "organization_id" = "ws"."organization_id" | |
| 7 | −FROM "workspaces" AS "ws" | |
| 8 | −WHERE "pol"."workspace_id" = "ws"."id";--> statement-breakpoint | |
| 9 | −ALTER TABLE "policies" ALTER COLUMN "scope_type" SET NOT NULL;--> statement-breakpoint | |
| 10 | −ALTER TABLE "policies" ALTER COLUMN "organization_id" SET NOT NULL;--> statement-breakpoint | |
| 11 | −ALTER TABLE "policies" ALTER COLUMN "workspace_id" DROP NOT NULL;--> statement-breakpoint | |
| 12 | −ALTER TABLE "policies" ADD CONSTRAINT "policies_scope_type_check" CHECK ("scope_type" in ('organization', 'workspace'));--> statement-breakpoint | |
| 13 | −ALTER TABLE "policies" ADD CONSTRAINT "policies_scope_consistency_check" CHECK (("scope_type" = 'organization' and "workspace_id" is null) or ("scope_type" = 'workspace' and "workspace_id" is not null));--> statement-breakpoint | |
| 14 | −DROP INDEX IF EXISTS "policies_workspace_priority_idx";--> statement-breakpoint | |
| 15 | −CREATE INDEX "policies_organization_priority_idx" ON "policies" ("organization_id","priority" DESC,"updated_at","id");--> statement-breakpoint | |
| 16 | −CREATE INDEX "policies_workspace_priority_idx" ON "policies" ("workspace_id","priority" DESC,"updated_at","id");--> statement-breakpoint | |
| 17 | −ALTER TABLE "execution_interactions" ADD COLUMN "purpose" text NOT NULL DEFAULT 'elicitation'; | |
| @@ -1,491 +0,0 @@ | ||
| 1 | −ALTER TABLE "sources" ADD COLUMN "recipe_id" text;--> statement-breakpoint | |
| 2 | −ALTER TABLE "sources" ADD COLUMN "recipe_revision_id" text;--> statement-breakpoint | |
| 3 | −ALTER TABLE "sources" ADD COLUMN "binding_config_json" text;--> statement-breakpoint | |
| 4 | − | |
| 5 | −CREATE TABLE "source_recipes" ( | |
| 6 | − "id" text PRIMARY KEY NOT NULL, | |
| 7 | − "kind" text NOT NULL, | |
| 8 | − "importer_kind" text NOT NULL, | |
| 9 | − "provider_key" text NOT NULL, | |
| 10 | − "name" text NOT NULL, | |
| 11 | − "summary" text, | |
| 12 | − "visibility" text NOT NULL, | |
| 13 | − "latest_revision_id" text NOT NULL, | |
| 14 | − "created_at" bigint NOT NULL, | |
| 15 | − "updated_at" bigint NOT NULL, | |
| 16 | − CONSTRAINT "source_recipes_kind_check" CHECK ("kind" in ('http_recipe', 'graphql_recipe', 'mcp_recipe', 'internal_recipe')), | |
| 17 | − CONSTRAINT "source_recipes_importer_kind_check" CHECK ("importer_kind" in ( | |
| 18 | − 'openapi', | |
| 19 | − 'google_discovery', | |
| 20 | − 'postman_collection', | |
| 21 | − 'snippet_bundle', | |
| 22 | − 'graphql_introspection', | |
| 23 | − 'mcp_manifest', | |
| 24 | − 'internal_manifest' | |
| 25 | − )), | |
| 26 | − CONSTRAINT "source_recipes_visibility_check" CHECK ("visibility" in ('private', 'workspace', 'organization', 'public')) | |
| 27 | −);--> statement-breakpoint | |
| 28 | − | |
| 29 | −CREATE TABLE "source_recipe_revisions" ( | |
| 30 | − "id" text PRIMARY KEY NOT NULL, | |
| 31 | − "recipe_id" text NOT NULL, | |
| 32 | − "revision_number" bigint NOT NULL, | |
| 33 | − "source_config_json" text NOT NULL, | |
| 34 | − "manifest_json" text, | |
| 35 | − "manifest_hash" text, | |
| 36 | − "created_at" bigint NOT NULL, | |
| 37 | − "updated_at" bigint NOT NULL | |
| 38 | −);--> statement-breakpoint | |
| 39 | − | |
| 40 | −CREATE TABLE "source_recipe_documents" ( | |
| 41 | − "id" text PRIMARY KEY NOT NULL, | |
| 42 | − "recipe_revision_id" text NOT NULL, | |
| 43 | − "document_kind" text NOT NULL, | |
| 44 | − "document_key" text NOT NULL, | |
| 45 | − "content_text" text NOT NULL, | |
| 46 | − "content_hash" text NOT NULL, | |
| 47 | − "fetched_at" bigint, | |
| 48 | − "created_at" bigint NOT NULL, | |
| 49 | − "updated_at" bigint NOT NULL, | |
| 50 | − CONSTRAINT "source_recipe_documents_kind_check" CHECK ("document_kind" in ( | |
| 51 | − 'google_discovery', | |
| 52 | − 'openapi', | |
| 53 | − 'postman_collection', | |
| 54 | − 'postman_environment', | |
| 55 | − 'graphql_introspection', | |
| 56 | − 'mcp_manifest' | |
| 57 | − )) | |
| 58 | −);--> statement-breakpoint | |
| 59 | − | |
| 60 | −CREATE TABLE "source_recipe_operations" ( | |
| 61 | − "id" text PRIMARY KEY NOT NULL, | |
| 62 | − "recipe_revision_id" text NOT NULL, | |
| 63 | − "operation_key" text NOT NULL, | |
| 64 | − "transport_kind" text NOT NULL, | |
| 65 | − "tool_id" text NOT NULL, | |
| 66 | − "title" text, | |
| 67 | − "description" text, | |
| 68 | − "operation_kind" text NOT NULL, | |
| 69 | − "search_text" text NOT NULL, | |
| 70 | − "input_schema_json" text, | |
| 71 | − "output_schema_json" text, | |
| 72 | − "provider_kind" text NOT NULL, | |
| 73 | − "provider_data_json" text, | |
| 74 | − "mcp_tool_name" text, | |
| 75 | − "openapi_method" text, | |
| 76 | − "openapi_path_template" text, | |
| 77 | − "openapi_operation_hash" text, | |
| 78 | − "openapi_raw_tool_id" text, | |
| 79 | − "openapi_operation_id" text, | |
| 80 | − "openapi_tags_json" text, | |
| 81 | − "openapi_request_body_required" boolean, | |
| 82 | − "graphql_operation_type" text, | |
| 83 | − "graphql_operation_name" text, | |
| 84 | − "created_at" bigint NOT NULL, | |
| 85 | − "updated_at" bigint NOT NULL, | |
| 86 | − CONSTRAINT "source_recipe_operations_transport_kind_check" CHECK ("transport_kind" in ('http', 'graphql', 'mcp', 'internal')), | |
| 87 | − CONSTRAINT "source_recipe_operations_kind_check" CHECK ("operation_kind" in ('read', 'write', 'delete', 'unknown')), | |
| 88 | − CONSTRAINT "source_recipe_operations_provider_kind_check" CHECK ("provider_kind" in ('mcp', 'openapi', 'graphql', 'internal')) | |
| 89 | −);--> statement-breakpoint | |
| 90 | − | |
| 91 | −CREATE TABLE "workspace_source_oauth_clients" ( | |
| 92 | − "id" text PRIMARY KEY NOT NULL, | |
| 93 | − "workspace_id" text NOT NULL, | |
| 94 | − "source_id" text NOT NULL, | |
| 95 | − "provider_key" text NOT NULL, | |
| 96 | − "client_id" text NOT NULL, | |
| 97 | − "client_secret_provider_id" text, | |
| 98 | − "client_secret_handle" text, | |
| 99 | − "client_metadata_json" text, | |
| 100 | − "created_at" bigint NOT NULL, | |
| 101 | − "updated_at" bigint NOT NULL | |
| 102 | −);--> statement-breakpoint | |
| 103 | − | |
| 104 | −UPDATE "sources" | |
| 105 | −SET | |
| 106 | − "recipe_id" = 'src_recipe_' || md5("workspace_id" || ':' || "source_id"), | |
| 107 | − "recipe_revision_id" = 'src_recipe_rev_' || md5("workspace_id" || ':' || "source_id"), | |
| 108 | − "binding_config_json" = null;--> statement-breakpoint | |
| 109 | − | |
| 110 | −INSERT INTO "source_recipes" ( | |
| 111 | − "id", | |
| 112 | − "kind", | |
| 113 | − "importer_kind", | |
| 114 | − "provider_key", | |
| 115 | − "name", | |
| 116 | − "summary", | |
| 117 | − "visibility", | |
| 118 | − "latest_revision_id", | |
| 119 | − "created_at", | |
| 120 | − "updated_at" | |
| 121 | −) | |
| 122 | −SELECT | |
| 123 | − "s"."recipe_id", | |
| 124 | − CASE | |
| 125 | − WHEN "s"."kind" = 'openapi' THEN 'http_recipe' | |
| 126 | − WHEN "s"."kind" = 'graphql' THEN 'graphql_recipe' | |
| 127 | − WHEN "s"."kind" = 'mcp' THEN 'mcp_recipe' | |
| 128 | − ELSE 'internal_recipe' | |
| 129 | − END, | |
| 130 | − CASE | |
| 131 | − WHEN "s"."kind" = 'openapi' THEN 'openapi' | |
| 132 | − WHEN "s"."kind" = 'graphql' THEN 'graphql_introspection' | |
| 133 | − WHEN "s"."kind" = 'mcp' THEN 'mcp_manifest' | |
| 134 | − ELSE 'internal_manifest' | |
| 135 | − END, | |
| 136 | − CASE | |
| 137 | − WHEN "s"."kind" = 'openapi' THEN 'generic_http' | |
| 138 | − WHEN "s"."kind" = 'graphql' THEN 'generic_graphql' | |
| 139 | − WHEN "s"."kind" = 'mcp' THEN 'generic_mcp' | |
| 140 | − ELSE 'generic_internal' | |
| 141 | − END, | |
| 142 | − "s"."name", | |
| 143 | − null, | |
| 144 | − 'workspace', | |
| 145 | − "s"."recipe_revision_id", | |
| 146 | − "s"."created_at", | |
| 147 | − "s"."updated_at" | |
| 148 | −FROM "sources" AS "s";--> statement-breakpoint | |
| 149 | − | |
| 150 | −INSERT INTO "source_recipe_revisions" ( | |
| 151 | − "id", | |
| 152 | − "recipe_id", | |
| 153 | − "revision_number", | |
| 154 | − "source_config_json", | |
| 155 | − "manifest_json", | |
| 156 | − "manifest_hash", | |
| 157 | − "created_at", | |
| 158 | − "updated_at" | |
| 159 | −) | |
| 160 | −SELECT | |
| 161 | − "s"."recipe_revision_id", | |
| 162 | − "s"."recipe_id", | |
| 163 | − 1, | |
| 164 | − CASE | |
| 165 | − WHEN "s"."kind" = 'mcp' THEN jsonb_build_object( | |
| 166 | − 'kind', 'mcp', | |
| 167 | − 'endpoint', "s"."endpoint", | |
| 168 | − 'transport', "s"."transport", | |
| 169 | − 'queryParams', COALESCE("s"."query_params_json"::jsonb, 'null'::jsonb), | |
| 170 | − 'headers', COALESCE("s"."headers_json"::jsonb, 'null'::jsonb) | |
| 171 | − )::text | |
| 172 | − WHEN "s"."kind" = 'openapi' THEN jsonb_build_object( | |
| 173 | − 'kind', 'openapi', | |
| 174 | − 'endpoint', "s"."endpoint", | |
| 175 | − 'specUrl', COALESCE("s"."spec_url", "s"."endpoint"), | |
| 176 | − 'defaultHeaders', COALESCE("s"."default_headers_json"::jsonb, 'null'::jsonb) | |
| 177 | − )::text | |
| 178 | − WHEN "s"."kind" = 'graphql' THEN jsonb_build_object( | |
| 179 | − 'kind', 'graphql', | |
| 180 | − 'endpoint', "s"."endpoint", | |
| 181 | − 'defaultHeaders', COALESCE("s"."default_headers_json"::jsonb, 'null'::jsonb) | |
| 182 | − )::text | |
| 183 | − ELSE jsonb_build_object( | |
| 184 | − 'kind', 'internal', | |
| 185 | − 'endpoint', "s"."endpoint" | |
| 186 | − )::text | |
| 187 | − END, | |
| 188 | − null, | |
| 189 | − "s"."source_hash", | |
| 190 | − "s"."created_at", | |
| 191 | − "s"."updated_at" | |
| 192 | −FROM "sources" AS "s";--> statement-breakpoint | |
| 193 | − | |
| 194 | −INSERT INTO "source_recipe_documents" ( | |
| 195 | − "id", | |
| 196 | − "recipe_revision_id", | |
| 197 | − "document_kind", | |
| 198 | − "document_key", | |
| 199 | − "content_text", | |
| 200 | − "content_hash", | |
| 201 | − "fetched_at", | |
| 202 | − "created_at", | |
| 203 | − "updated_at" | |
| 204 | −) | |
| 205 | −SELECT | |
| 206 | − 'src_recipe_doc_' || md5("s"."workspace_id" || ':' || "s"."source_id" || ':primary'), | |
| 207 | − "s"."recipe_revision_id", | |
| 208 | − CASE | |
| 209 | − WHEN "s"."kind" = 'openapi' THEN 'openapi' | |
| 210 | − WHEN "s"."kind" = 'graphql' THEN 'graphql_introspection' | |
| 211 | − ELSE 'mcp_manifest' | |
| 212 | − END, | |
| 213 | − CASE | |
| 214 | − WHEN "s"."kind" = 'openapi' THEN COALESCE("s"."spec_url", "s"."endpoint") | |
| 215 | − ELSE "s"."endpoint" | |
| 216 | − END, | |
| 217 | − "s"."source_document_text", | |
| 218 | − md5("s"."source_document_text"), | |
| 219 | − "s"."updated_at", | |
| 220 | − "s"."created_at", | |
| 221 | − "s"."updated_at" | |
| 222 | −FROM "sources" AS "s" | |
| 223 | −WHERE "s"."source_document_text" IS NOT NULL;--> statement-breakpoint | |
| 224 | − | |
| 225 | −INSERT INTO "source_recipe_operations" ( | |
| 226 | − "id", | |
| 227 | − "recipe_revision_id", | |
| 228 | − "operation_key", | |
| 229 | − "transport_kind", | |
| 230 | − "tool_id", | |
| 231 | − "title", | |
| 232 | − "description", | |
| 233 | − "operation_kind", | |
| 234 | − "search_text", | |
| 235 | − "input_schema_json", | |
| 236 | − "output_schema_json", | |
| 237 | − "provider_kind", | |
| 238 | − "provider_data_json", | |
| 239 | − "mcp_tool_name", | |
| 240 | − "openapi_method", | |
| 241 | − "openapi_path_template", | |
| 242 | − "openapi_operation_hash", | |
| 243 | − "openapi_raw_tool_id", | |
| 244 | − "openapi_operation_id", | |
| 245 | − "openapi_tags_json", | |
| 246 | − "openapi_request_body_required", | |
| 247 | − "graphql_operation_type", | |
| 248 | − "graphql_operation_name", | |
| 249 | − "created_at", | |
| 250 | − "updated_at" | |
| 251 | −) | |
| 252 | −SELECT | |
| 253 | − 'src_recipe_op_' || md5("ta"."workspace_id" || ':' || "ta"."source_id" || ':' || "ta"."path"), | |
| 254 | − "s"."recipe_revision_id", | |
| 255 | − "ta"."tool_id", | |
| 256 | − CASE | |
| 257 | − WHEN "ta"."provider_kind" = 'openapi' THEN 'http' | |
| 258 | − ELSE 'mcp' | |
| 259 | − END, | |
| 260 | − "ta"."tool_id", | |
| 261 | − "ta"."title", | |
| 262 | − "ta"."description", | |
| 263 | − CASE | |
| 264 | − WHEN "ta"."provider_kind" = 'openapi' AND upper(COALESCE("ta"."openapi_method", '')) IN ('GET', 'HEAD') THEN 'read' | |
| 265 | − WHEN "ta"."provider_kind" = 'openapi' AND upper(COALESCE("ta"."openapi_method", '')) = 'DELETE' THEN 'delete' | |
| 266 | − WHEN "ta"."provider_kind" = 'openapi' THEN 'write' | |
| 267 | − ELSE 'unknown' | |
| 268 | − END, | |
| 269 | − "ta"."search_text", | |
| 270 | − "ta"."input_schema_json", | |
| 271 | − "ta"."output_schema_json", | |
| 272 | − "ta"."provider_kind", | |
| 273 | − CASE | |
| 274 | − WHEN "ta"."provider_kind" = 'mcp' THEN jsonb_build_object( | |
| 275 | − 'kind', 'mcp', | |
| 276 | − 'toolId', "ta"."tool_id", | |
| 277 | − 'toolName', COALESCE("ta"."mcp_tool_name", "ta"."title", "ta"."path"), | |
| 278 | − 'description', "ta"."description" | |
| 279 | − )::text | |
| 280 | − ELSE jsonb_build_object( | |
| 281 | − 'kind', 'openapi', | |
| 282 | − 'toolId', "ta"."tool_id", | |
| 283 | − 'rawToolId', "ta"."openapi_raw_tool_id", | |
| 284 | − 'operationId', "ta"."openapi_operation_id", | |
| 285 | − 'tags', COALESCE("ta"."openapi_tags_json"::jsonb, '[]'::jsonb), | |
| 286 | − 'method', "ta"."openapi_method", | |
| 287 | − 'path', "ta"."openapi_path_template", | |
| 288 | − 'operationHash', "ta"."openapi_operation_hash" | |
| 289 | − )::text | |
| 290 | − END, | |
| 291 | − "ta"."mcp_tool_name", | |
| 292 | − "ta"."openapi_method", | |
| 293 | − "ta"."openapi_path_template", | |
| 294 | − "ta"."openapi_operation_hash", | |
| 295 | − "ta"."openapi_raw_tool_id", | |
| 296 | − "ta"."openapi_operation_id", | |
| 297 | − "ta"."openapi_tags_json", | |
| 298 | − "ta"."openapi_request_body_required", | |
| 299 | − null, | |
| 300 | − null, | |
| 301 | − "ta"."created_at", | |
| 302 | − "ta"."updated_at" | |
| 303 | −FROM "tool_artifacts" AS "ta" | |
| 304 | −JOIN "sources" AS "s" | |
| 305 | − ON "s"."workspace_id" = "ta"."workspace_id" | |
| 306 | − AND "s"."source_id" = "ta"."source_id";--> statement-breakpoint | |
| 307 | − | |
| 308 | −WITH "mcp_manifests" AS ( | |
| 309 | − SELECT | |
| 310 | − "s"."workspace_id", | |
| 311 | − "s"."source_id", | |
| 312 | − "s"."recipe_revision_id", | |
| 313 | − jsonb_build_object( | |
| 314 | − 'version', 1, | |
| 315 | − 'tools', COALESCE( | |
| 316 | − jsonb_agg( | |
| 317 | − jsonb_build_object( | |
| 318 | − 'toolId', "ta"."tool_id", | |
| 319 | − 'toolName', COALESCE("ta"."mcp_tool_name", "ta"."title", "ta"."path"), | |
| 320 | − 'description', "ta"."description", | |
| 321 | − 'inputSchemaJson', "ta"."input_schema_json", | |
| 322 | − 'outputSchemaJson', "ta"."output_schema_json" | |
| 323 | − ) | |
| 324 | − ORDER BY "ta"."path" | |
| 325 | − ) FILTER (WHERE "ta"."provider_kind" = 'mcp'), | |
| 326 | − '[]'::jsonb | |
| 327 | − ) | |
| 328 | − )::text AS "manifest_json" | |
| 329 | − FROM "sources" AS "s" | |
| 330 | − LEFT JOIN "tool_artifacts" AS "ta" | |
| 331 | − ON "ta"."workspace_id" = "s"."workspace_id" | |
| 332 | − AND "ta"."source_id" = "s"."source_id" | |
| 333 | − WHERE "s"."kind" = 'mcp' | |
| 334 | − GROUP BY "s"."workspace_id", "s"."source_id", "s"."recipe_revision_id" | |
| 335 | −) | |
| 336 | −UPDATE "source_recipe_revisions" AS "rev" | |
| 337 | −SET | |
| 338 | − "manifest_json" = "mcp"."manifest_json", | |
| 339 | − "manifest_hash" = md5("mcp"."manifest_json") | |
| 340 | −FROM "mcp_manifests" AS "mcp" | |
| 341 | −WHERE "rev"."id" = "mcp"."recipe_revision_id";--> statement-breakpoint | |
| 342 | − | |
| 343 | −INSERT INTO "source_recipe_documents" ( | |
| 344 | − "id", | |
| 345 | − "recipe_revision_id", | |
| 346 | − "document_kind", | |
| 347 | − "document_key", | |
| 348 | − "content_text", | |
| 349 | − "content_hash", | |
| 350 | − "fetched_at", | |
| 351 | − "created_at", | |
| 352 | − "updated_at" | |
| 353 | −) | |
| 354 | −SELECT | |
| 355 | − 'src_recipe_doc_' || md5("s"."workspace_id" || ':' || "s"."source_id" || ':mcp_manifest'), | |
| 356 | − "s"."recipe_revision_id", | |
| 357 | − 'mcp_manifest', | |
| 358 | − "s"."endpoint", | |
| 359 | − "rev"."manifest_json", | |
| 360 | − md5("rev"."manifest_json"), | |
| 361 | − "s"."updated_at", | |
| 362 | − "s"."created_at", | |
| 363 | − "s"."updated_at" | |
| 364 | −FROM "sources" AS "s" | |
| 365 | −JOIN "source_recipe_revisions" AS "rev" | |
| 366 | − ON "rev"."id" = "s"."recipe_revision_id" | |
| 367 | −WHERE "s"."kind" = 'mcp' | |
| 368 | − AND "rev"."manifest_json" IS NOT NULL | |
| 369 | − AND NOT EXISTS ( | |
| 370 | − SELECT 1 | |
| 371 | − FROM "source_recipe_documents" AS "doc" | |
| 372 | − WHERE "doc"."recipe_revision_id" = "s"."recipe_revision_id" | |
| 373 | − AND "doc"."document_kind" = 'mcp_manifest' | |
| 374 | − AND "doc"."document_key" = "s"."endpoint" | |
| 375 | − );--> statement-breakpoint | |
| 376 | − | |
| 377 | −ALTER TABLE "sources" ALTER COLUMN "recipe_id" SET NOT NULL;--> statement-breakpoint | |
| 378 | −ALTER TABLE "sources" ALTER COLUMN "recipe_revision_id" SET NOT NULL;--> statement-breakpoint | |
| 379 | −CREATE INDEX "sources_workspace_recipe_idx" ON "sources" ("workspace_id","recipe_id","updated_at","source_id");--> statement-breakpoint | |
| 380 | − | |
| 381 | −CREATE TABLE "workspace_source_credentials" ( | |
| 382 | − "id" text PRIMARY KEY NOT NULL, | |
| 383 | − "workspace_id" text NOT NULL, | |
| 384 | − "source_id" text NOT NULL, | |
| 385 | − "actor_account_id" text, | |
| 386 | − "auth_kind" text NOT NULL, | |
| 387 | − "auth_header_name" text NOT NULL, | |
| 388 | − "auth_prefix" text NOT NULL, | |
| 389 | − "token_provider_id" text NOT NULL, | |
| 390 | − "token_handle" text NOT NULL, | |
| 391 | − "refresh_token_provider_id" text, | |
| 392 | − "refresh_token_handle" text, | |
| 393 | − "created_at" bigint NOT NULL, | |
| 394 | − "updated_at" bigint NOT NULL, | |
| 395 | − CONSTRAINT "credentials_auth_kind_check" CHECK ("auth_kind" in ('bearer', 'oauth2')) | |
| 396 | −);--> statement-breakpoint | |
| 397 | − | |
| 398 | −INSERT INTO "workspace_source_credentials" ( | |
| 399 | − "id", | |
| 400 | − "workspace_id", | |
| 401 | − "source_id", | |
| 402 | − "actor_account_id", | |
| 403 | − "auth_kind", | |
| 404 | − "auth_header_name", | |
| 405 | − "auth_prefix", | |
| 406 | − "token_provider_id", | |
| 407 | − "token_handle", | |
| 408 | − "refresh_token_provider_id", | |
| 409 | − "refresh_token_handle", | |
| 410 | − "created_at", | |
| 411 | − "updated_at" | |
| 412 | −) | |
| 413 | −SELECT | |
| 414 | − CASE | |
| 415 | − WHEN count(*) OVER (PARTITION BY "c"."id") = 1 THEN "c"."id" | |
| 416 | − ELSE "c"."id" || ':' || "scb"."source_id" | |
| 417 | − END, | |
| 418 | − "c"."workspace_id", | |
| 419 | − "scb"."source_id", | |
| 420 | − null, | |
| 421 | − "c"."auth_kind", | |
| 422 | − "c"."auth_header_name", | |
| 423 | − "c"."auth_prefix", | |
| 424 | − "c"."token_provider_id", | |
| 425 | − "c"."token_handle", | |
| 426 | − "c"."refresh_token_provider_id", | |
| 427 | − "c"."refresh_token_handle", | |
| 428 | − "c"."created_at", | |
| 429 | − "c"."updated_at" | |
| 430 | −FROM "credentials" AS "c" | |
| 431 | −JOIN "source_credential_bindings" AS "scb" | |
| 432 | − ON "scb"."workspace_id" = "c"."workspace_id" | |
| 433 | − AND "scb"."credential_id" = "c"."id";--> statement-breakpoint | |
| 434 | − | |
| 435 | −DROP TABLE "credentials";--> statement-breakpoint | |
| 436 | −DROP TABLE "source_credential_bindings";--> statement-breakpoint | |
| 437 | −CREATE INDEX "credentials_workspace_idx" ON "workspace_source_credentials" ("workspace_id","updated_at","id");--> statement-breakpoint | |
| 438 | −CREATE UNIQUE INDEX "credentials_workspace_source_actor_idx" ON "workspace_source_credentials" ("workspace_id","source_id","actor_account_id");--> statement-breakpoint | |
| 439 | −CREATE INDEX "credentials_workspace_source_idx" ON "workspace_source_credentials" ("workspace_id","source_id","updated_at","id");--> statement-breakpoint | |
| 440 | − | |
| 441 | −ALTER TABLE "source_auth_sessions" ADD COLUMN "actor_account_id" text;--> statement-breakpoint | |
| 442 | −ALTER TABLE "source_auth_sessions" ADD COLUMN "provider_kind" text;--> statement-breakpoint | |
| 443 | −ALTER TABLE "source_auth_sessions" ADD COLUMN "session_data_json" text;--> statement-breakpoint | |
| 444 | − | |
| 445 | −UPDATE "source_auth_sessions" | |
| 446 | −SET | |
| 447 | − "provider_kind" = 'mcp_oauth', | |
| 448 | − "session_data_json" = jsonb_build_object( | |
| 449 | − 'kind', 'mcp_oauth', | |
| 450 | − 'endpoint', "endpoint", | |
| 451 | − 'redirectUri', "redirect_uri", | |
| 452 | − 'scope', "scope", | |
| 453 | − 'resourceMetadataUrl', "resource_metadata_url", | |
| 454 | − 'authorizationServerUrl', "authorization_server_url", | |
| 455 | − 'resourceMetadataJson', "resource_metadata_json", | |
| 456 | − 'authorizationServerMetadataJson', "authorization_server_metadata_json", | |
| 457 | − 'clientInformationJson', "client_information_json", | |
| 458 | − 'codeVerifier', "code_verifier", | |
| 459 | − 'authorizationUrl', "authorization_url" | |
| 460 | − )::text;--> statement-breakpoint | |
| 461 | − | |
| 462 | −ALTER TABLE "source_auth_sessions" ALTER COLUMN "provider_kind" SET NOT NULL;--> statement-breakpoint | |
| 463 | −ALTER TABLE "source_auth_sessions" ALTER COLUMN "session_data_json" SET NOT NULL;--> statement-breakpoint | |
| 464 | −DROP INDEX IF EXISTS "source_auth_sessions_pending_idx";--> statement-breakpoint | |
| 465 | −ALTER TABLE "source_auth_sessions" DROP CONSTRAINT "source_auth_sessions_strategy_check";--> statement-breakpoint | |
| 466 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "strategy";--> statement-breakpoint | |
| 467 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "endpoint";--> statement-breakpoint | |
| 468 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "redirect_uri";--> statement-breakpoint | |
| 469 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "scope";--> statement-breakpoint | |
| 470 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "resource_metadata_url";--> statement-breakpoint | |
| 471 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "authorization_server_url";--> statement-breakpoint | |
| 472 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "resource_metadata_json";--> statement-breakpoint | |
| 473 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "authorization_server_metadata_json";--> statement-breakpoint | |
| 474 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "client_information_json";--> statement-breakpoint | |
| 475 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "code_verifier";--> statement-breakpoint | |
| 476 | −ALTER TABLE "source_auth_sessions" DROP COLUMN "authorization_url";--> statement-breakpoint | |
| 477 | −ALTER TABLE "source_auth_sessions" ADD CONSTRAINT "source_auth_sessions_provider_kind_check" CHECK ("provider_kind" in ('mcp_oauth', 'oauth2_pkce'));--> statement-breakpoint | |
| 478 | −CREATE INDEX "source_auth_sessions_pending_idx" ON "source_auth_sessions" ("workspace_id","source_id","actor_account_id","status","updated_at","id");--> statement-breakpoint | |
| 479 | − | |
| 480 | −CREATE INDEX "source_recipes_provider_updated_idx" ON "source_recipes" ("provider_key","updated_at","id");--> statement-breakpoint | |
| 481 | −CREATE INDEX "source_recipes_visibility_updated_idx" ON "source_recipes" ("visibility","updated_at","id");--> statement-breakpoint | |
| 482 | −CREATE UNIQUE INDEX "source_recipe_revisions_recipe_revision_idx" ON "source_recipe_revisions" ("recipe_id","revision_number");--> statement-breakpoint | |
| 483 | −CREATE UNIQUE INDEX "source_recipe_revisions_recipe_manifest_idx" ON "source_recipe_revisions" ("recipe_id","manifest_hash");--> statement-breakpoint | |
| 484 | −CREATE INDEX "source_recipe_revisions_recipe_created_idx" ON "source_recipe_revisions" ("recipe_id","created_at","id");--> statement-breakpoint | |
| 485 | −CREATE UNIQUE INDEX "source_recipe_documents_revision_kind_key_idx" ON "source_recipe_documents" ("recipe_revision_id","document_kind","document_key");--> statement-breakpoint | |
| 486 | −CREATE INDEX "source_recipe_documents_revision_created_idx" ON "source_recipe_documents" ("recipe_revision_id","created_at","id");--> statement-breakpoint | |
| 487 | −CREATE UNIQUE INDEX "source_recipe_operations_revision_operation_key_idx" ON "source_recipe_operations" ("recipe_revision_id","operation_key");--> statement-breakpoint | |
| 488 | −CREATE INDEX "source_recipe_operations_revision_tool_idx" ON "source_recipe_operations" ("recipe_revision_id","tool_id","updated_at","id");--> statement-breakpoint | |
| 489 | −CREATE INDEX "source_recipe_operations_search_text_idx" ON "source_recipe_operations" USING gin (to_tsvector('simple', "search_text"));--> statement-breakpoint | |
| 490 | −CREATE UNIQUE INDEX "workspace_source_oauth_clients_workspace_source_provider_idx" ON "workspace_source_oauth_clients" ("workspace_id","source_id","provider_key");--> statement-breakpoint | |
| 491 | −CREATE INDEX "workspace_source_oauth_clients_workspace_idx" ON "workspace_source_oauth_clients" ("workspace_id","updated_at","source_id"); | |
| @@ -1,14 +0,0 @@ | ||
| 1 | −CREATE TABLE "source_recipe_schema_bundles" ( | |
| 2 | − "id" text PRIMARY KEY NOT NULL, | |
| 3 | − "recipe_revision_id" text NOT NULL, | |
| 4 | − "bundle_kind" text NOT NULL, | |
| 5 | − "refs_json" text NOT NULL, | |
| 6 | − "content_hash" text NOT NULL, | |
| 7 | − "created_at" bigint NOT NULL, | |
| 8 | − "updated_at" bigint NOT NULL, | |
| 9 | − CONSTRAINT "source_recipe_schema_bundles_kind_check" CHECK ("source_recipe_schema_bundles"."bundle_kind" in ('json_schema_ref_map')) | |
| 10 | −); | |
| 11 | −--> statement-breakpoint | |
| 12 | −CREATE UNIQUE INDEX "source_recipe_schema_bundles_revision_kind_idx" ON "source_recipe_schema_bundles" USING btree ("recipe_revision_id","bundle_kind"); | |
| 13 | −--> statement-breakpoint | |
| 14 | −CREATE INDEX "source_recipe_schema_bundles_revision_created_idx" ON "source_recipe_schema_bundles" USING btree ("recipe_revision_id","created_at","id"); | |
| @@ -1,9 +0,0 @@ | ||
| 1 | −ALTER TABLE "sources" DROP CONSTRAINT IF EXISTS "sources_kind_check"; | |
| 2 | −--> statement-breakpoint | |
| 3 | −ALTER TABLE "source_recipes" DROP CONSTRAINT IF EXISTS "source_recipes_importer_kind_check"; | |
| 4 | −--> statement-breakpoint | |
| 5 | −ALTER TABLE "source_recipe_documents" DROP CONSTRAINT IF EXISTS "source_recipe_documents_kind_check"; | |
| 6 | −--> statement-breakpoint | |
| 7 | −ALTER TABLE "source_recipe_schema_bundles" DROP CONSTRAINT IF EXISTS "source_recipe_schema_bundles_kind_check"; | |
| 8 | −--> statement-breakpoint | |
| 9 | −ALTER TABLE "source_recipe_operations" DROP CONSTRAINT IF EXISTS "source_recipe_operations_provider_kind_check"; | |
| @@ -1,23 +0,0 @@ | ||
| 1 | −ALTER TABLE "source_recipes" | |
| 2 | −DROP CONSTRAINT IF EXISTS "source_recipes_kind_check";--> statement-breakpoint | |
| 3 | − | |
| 4 | −UPDATE "source_recipes" | |
| 5 | −SET "kind" = 'http_api' | |
| 6 | −WHERE "kind" IN ('http_recipe', 'graphql_recipe');--> statement-breakpoint | |
| 7 | − | |
| 8 | −UPDATE "source_recipes" | |
| 9 | −SET "kind" = 'mcp' | |
| 10 | −WHERE "kind" = 'mcp_recipe';--> statement-breakpoint | |
| 11 | − | |
| 12 | −UPDATE "source_recipes" | |
| 13 | −SET "kind" = 'internal' | |
| 14 | −WHERE "kind" = 'internal_recipe';--> statement-breakpoint | |
| 15 | − | |
| 16 | −ALTER TABLE "source_recipes" | |
| 17 | −ADD CONSTRAINT "source_recipes_kind_check" | |
| 18 | −CHECK ("kind" in ('http_api', 'mcp', 'internal'));--> statement-breakpoint | |
| 19 | − | |
| 20 | −CREATE TABLE IF NOT EXISTS "control_plane_data_migrations" ( | |
| 21 | − "id" text PRIMARY KEY NOT NULL, | |
| 22 | − "applied_at" bigint NOT NULL | |
| 23 | −); | |
| @@ -1,62 +0,0 @@ | ||
| 1 | −UPDATE "sources" | |
| 2 | −SET "binding_config_json" = CASE | |
| 3 | − WHEN "binding_config_json" IS NOT NULL AND btrim("binding_config_json") <> '' THEN "binding_config_json" | |
| 4 | − WHEN "kind" = 'openapi' THEN jsonb_build_object( | |
| 5 | − 'adapterKey', 'openapi', | |
| 6 | − 'specUrl', COALESCE("spec_url", "endpoint"), | |
| 7 | − 'defaultHeaders', | |
| 8 | − CASE | |
| 9 | − WHEN "default_headers_json" IS NULL OR btrim("default_headers_json") = '' THEN NULL | |
| 10 | − ELSE "default_headers_json"::jsonb | |
| 11 | − END | |
| 12 | − )::text | |
| 13 | − WHEN "kind" = 'graphql' THEN jsonb_build_object( | |
| 14 | − 'adapterKey', 'graphql', | |
| 15 | − 'defaultHeaders', | |
| 16 | − CASE | |
| 17 | − WHEN "default_headers_json" IS NULL OR btrim("default_headers_json") = '' THEN NULL | |
| 18 | − ELSE "default_headers_json"::jsonb | |
| 19 | − END | |
| 20 | − )::text | |
| 21 | − WHEN "kind" = 'mcp' THEN jsonb_build_object( | |
| 22 | − 'adapterKey', 'mcp', | |
| 23 | − 'transport', "transport", | |
| 24 | − 'queryParams', | |
| 25 | − CASE | |
| 26 | − WHEN "query_params_json" IS NULL OR btrim("query_params_json") = '' THEN NULL | |
| 27 | − ELSE "query_params_json"::jsonb | |
| 28 | − END, | |
| 29 | − 'headers', | |
| 30 | − CASE | |
| 31 | − WHEN "headers_json" IS NULL OR btrim("headers_json") = '' THEN NULL | |
| 32 | − ELSE "headers_json"::jsonb | |
| 33 | − END | |
| 34 | − )::text | |
| 35 | − ELSE jsonb_build_object( | |
| 36 | − 'adapterKey', "kind" | |
| 37 | − )::text | |
| 38 | −END;--> statement-breakpoint | |
| 39 | − | |
| 40 | −ALTER TABLE "sources" | |
| 41 | −ALTER COLUMN "binding_config_json" SET NOT NULL;--> statement-breakpoint | |
| 42 | − | |
| 43 | −ALTER TABLE "sources" | |
| 44 | −DROP CONSTRAINT IF EXISTS "sources_transport_check";--> statement-breakpoint | |
| 45 | − | |
| 46 | −ALTER TABLE "sources" | |
| 47 | −DROP COLUMN IF EXISTS "transport";--> statement-breakpoint | |
| 48 | − | |
| 49 | −ALTER TABLE "sources" | |
| 50 | −DROP COLUMN IF EXISTS "query_params_json";--> statement-breakpoint | |
| 51 | − | |
| 52 | −ALTER TABLE "sources" | |
| 53 | −DROP COLUMN IF EXISTS "headers_json";--> statement-breakpoint | |
| 54 | − | |
| 55 | −ALTER TABLE "sources" | |
| 56 | −DROP COLUMN IF EXISTS "spec_url";--> statement-breakpoint | |
| 57 | − | |
| 58 | −ALTER TABLE "sources" | |
| 59 | −DROP COLUMN IF EXISTS "default_headers_json";--> statement-breakpoint | |
| 60 | − | |
| 61 | −ALTER TABLE "sources" | |
| 62 | −DROP COLUMN IF EXISTS "source_document_text"; | |
| @@ -1,82 +0,0 @@ | ||
| 1 | −ALTER TABLE "sources" ADD COLUMN "import_auth_policy" text;--> statement-breakpoint | |
| 2 | − | |
| 3 | −UPDATE "sources" | |
| 4 | −SET "import_auth_policy" = CASE | |
| 5 | − WHEN "kind" = 'internal' THEN 'none' | |
| 6 | − ELSE 'reuse_runtime' | |
| 7 | −END | |
| 8 | −WHERE "import_auth_policy" IS NULL;--> statement-breakpoint | |
| 9 | − | |
| 10 | −ALTER TABLE "sources" | |
| 11 | −ALTER COLUMN "import_auth_policy" SET NOT NULL;--> statement-breakpoint | |
| 12 | − | |
| 13 | −ALTER TABLE "sources" | |
| 14 | −DROP CONSTRAINT IF EXISTS "sources_import_auth_policy_check";--> statement-breakpoint | |
| 15 | − | |
| 16 | −ALTER TABLE "sources" | |
| 17 | −ADD CONSTRAINT "sources_import_auth_policy_check" | |
| 18 | −CHECK ("import_auth_policy" in ('none', 'reuse_runtime', 'separate'));--> statement-breakpoint | |
| 19 | − | |
| 20 | −ALTER TABLE "source_recipe_revisions" ADD COLUMN "materialization_hash" text;--> statement-breakpoint | |
| 21 | − | |
| 22 | −UPDATE "source_recipe_revisions" | |
| 23 | −SET "materialization_hash" = "manifest_hash" | |
| 24 | −WHERE "materialization_hash" IS NULL;--> statement-breakpoint | |
| 25 | − | |
| 26 | −DROP INDEX IF EXISTS "source_recipe_revisions_recipe_manifest_idx";--> statement-breakpoint | |
| 27 | − | |
| 28 | −CREATE UNIQUE INDEX "source_recipe_revisions_recipe_materialization_idx" | |
| 29 | −ON "source_recipe_revisions" ("recipe_id","materialization_hash");--> statement-breakpoint | |
| 30 | − | |
| 31 | −CREATE INDEX "source_recipe_revisions_recipe_manifest_idx" | |
| 32 | −ON "source_recipe_revisions" ("recipe_id","manifest_hash");--> statement-breakpoint | |
| 33 | − | |
| 34 | −ALTER TABLE "workspace_source_credentials" ADD COLUMN "slot" text;--> statement-breakpoint | |
| 35 | − | |
| 36 | −UPDATE "workspace_source_credentials" | |
| 37 | −SET "slot" = 'runtime' | |
| 38 | −WHERE "slot" IS NULL;--> statement-breakpoint | |
| 39 | − | |
| 40 | −ALTER TABLE "workspace_source_credentials" | |
| 41 | −ALTER COLUMN "slot" SET NOT NULL;--> statement-breakpoint | |
| 42 | − | |
| 43 | −DROP INDEX IF EXISTS "credentials_workspace_source_actor_idx";--> statement-breakpoint | |
| 44 | − | |
| 45 | −ALTER TABLE "workspace_source_credentials" | |
| 46 | −DROP CONSTRAINT IF EXISTS "credentials_slot_check";--> statement-breakpoint | |
| 47 | − | |
| 48 | −ALTER TABLE "workspace_source_credentials" | |
| 49 | −ADD CONSTRAINT "credentials_slot_check" | |
| 50 | −CHECK ("slot" in ('runtime', 'import'));--> statement-breakpoint | |
| 51 | − | |
| 52 | −CREATE UNIQUE INDEX "credentials_workspace_source_actor_idx" | |
| 53 | −ON "workspace_source_credentials" ("workspace_id","source_id","actor_account_id","slot");--> statement-breakpoint | |
| 54 | − | |
| 55 | −ALTER TABLE "source_auth_sessions" ADD COLUMN "credential_slot" text;--> statement-breakpoint | |
| 56 | − | |
| 57 | −UPDATE "source_auth_sessions" | |
| 58 | −SET "credential_slot" = 'runtime' | |
| 59 | −WHERE "credential_slot" IS NULL;--> statement-breakpoint | |
| 60 | − | |
| 61 | −ALTER TABLE "source_auth_sessions" | |
| 62 | −ALTER COLUMN "credential_slot" SET NOT NULL;--> statement-breakpoint | |
| 63 | − | |
| 64 | −DROP INDEX IF EXISTS "source_auth_sessions_pending_idx";--> statement-breakpoint | |
| 65 | − | |
| 66 | −ALTER TABLE "source_auth_sessions" | |
| 67 | −DROP CONSTRAINT IF EXISTS "source_auth_sessions_credential_slot_check";--> statement-breakpoint | |
| 68 | − | |
| 69 | −ALTER TABLE "source_auth_sessions" | |
| 70 | −ADD CONSTRAINT "source_auth_sessions_credential_slot_check" | |
| 71 | −CHECK ("credential_slot" in ('runtime', 'import'));--> statement-breakpoint | |
| 72 | − | |
| 73 | −CREATE INDEX "source_auth_sessions_pending_idx" | |
| 74 | −ON "source_auth_sessions" ( | |
| 75 | − "workspace_id", | |
| 76 | − "source_id", | |
| 77 | − "actor_account_id", | |
| 78 | − "credential_slot", | |
| 79 | − "status", | |
| 80 | − "updated_at", | |
| 81 | − "id" | |
| 82 | −);--> statement-breakpoint | |
| @@ -1,73 +0,0 @@ | ||
| 1 | −UPDATE "source_recipe_operations" | |
| 2 | −SET "provider_data_json" = CASE | |
| 3 | − WHEN "provider_kind" = 'openapi' THEN jsonb_strip_nulls(jsonb_build_object( | |
| 4 | − 'kind', 'openapi', | |
| 5 | − 'toolId', "tool_id", | |
| 6 | − 'rawToolId', COALESCE("openapi_raw_tool_id", "tool_id"), | |
| 7 | − 'operationId', "openapi_operation_id", | |
| 8 | − 'group', CASE | |
| 9 | − WHEN position('.' in "tool_id") > 0 THEN split_part("tool_id", '.', 1) | |
| 10 | − ELSE "tool_id" | |
| 11 | − END, | |
| 12 | − 'leaf', CASE | |
| 13 | − WHEN position('.' in "tool_id") > 0 THEN substring("tool_id" from position('.' in "tool_id") + 1) | |
| 14 | − ELSE "tool_id" | |
| 15 | − END, | |
| 16 | − 'tags', CASE | |
| 17 | − WHEN "openapi_tags_json" IS NOT NULL THEN "openapi_tags_json"::jsonb | |
| 18 | − ELSE '[]'::jsonb | |
| 19 | − END, | |
| 20 | − 'method', COALESCE("openapi_method", 'get'), | |
| 21 | − 'path', COALESCE("openapi_path_template", '/'), | |
| 22 | − 'operationHash', COALESCE( | |
| 23 | − "openapi_operation_hash", | |
| 24 | − md5(COALESCE("tool_id", '') || ':' || COALESCE("openapi_path_template", '/')) | |
| 25 | − ), | |
| 26 | − 'invocation', jsonb_build_object( | |
| 27 | − 'method', COALESCE("openapi_method", 'get'), | |
| 28 | − 'pathTemplate', COALESCE("openapi_path_template", '/'), | |
| 29 | − 'parameters', '[]'::jsonb, | |
| 30 | − 'requestBody', CASE | |
| 31 | − WHEN "openapi_request_body_required" IS NULL THEN null | |
| 32 | − ELSE jsonb_build_object( | |
| 33 | − 'required', "openapi_request_body_required", | |
| 34 | − 'contentTypes', '[]'::jsonb | |
| 35 | − ) | |
| 36 | − END | |
| 37 | − ) | |
| 38 | − ))::text | |
| 39 | − WHEN "provider_kind" = 'graphql' THEN jsonb_build_object( | |
| 40 | − 'kind', 'graphql', | |
| 41 | − 'toolKind', 'field', | |
| 42 | − 'toolId', "tool_id", | |
| 43 | − 'rawToolId', null, | |
| 44 | − 'group', null, | |
| 45 | − 'leaf', null, | |
| 46 | − 'fieldName', null, | |
| 47 | − 'operationType', "graphql_operation_type", | |
| 48 | − 'operationName', "graphql_operation_name", | |
| 49 | − 'operationDocument', null, | |
| 50 | − 'queryTypeName', null, | |
| 51 | − 'mutationTypeName', null, | |
| 52 | − 'subscriptionTypeName', null | |
| 53 | − )::text | |
| 54 | − WHEN "provider_kind" = 'mcp' THEN jsonb_build_object( | |
| 55 | − 'kind', 'mcp', | |
| 56 | − 'toolId', "tool_id", | |
| 57 | − 'toolName', COALESCE("mcp_tool_name", "title", "tool_id"), | |
| 58 | − 'description', "description" | |
| 59 | − )::text | |
| 60 | − ELSE "provider_data_json" | |
| 61 | −END | |
| 62 | −WHERE "provider_data_json" IS NULL;--> statement-breakpoint | |
| 63 | − | |
| 64 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "mcp_tool_name";--> statement-breakpoint | |
| 65 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_method";--> statement-breakpoint | |
| 66 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_path_template";--> statement-breakpoint | |
| 67 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_operation_hash";--> statement-breakpoint | |
| 68 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_raw_tool_id";--> statement-breakpoint | |
| 69 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_operation_id";--> statement-breakpoint | |
| 70 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_tags_json";--> statement-breakpoint | |
| 71 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "openapi_request_body_required";--> statement-breakpoint | |
| 72 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "graphql_operation_type";--> statement-breakpoint | |
| 73 | −ALTER TABLE "source_recipe_operations" DROP COLUMN "graphql_operation_name";--> statement-breakpoint | |
| @@ -1,7 +0,0 @@ | ||
| 1 | −DROP TABLE IF EXISTS "tool_artifact_parameters"; | |
| 2 | −--> statement-breakpoint | |
| 3 | −DROP TABLE IF EXISTS "tool_artifact_request_body_content_types"; | |
| 4 | −--> statement-breakpoint | |
| 5 | −DROP TABLE IF EXISTS "tool_artifact_ref_hint_keys"; | |
| 6 | −--> statement-breakpoint | |
| 7 | −DROP TABLE IF EXISTS "tool_artifacts"; | |
| @@ -1,55 +0,0 @@ | ||
| 1 | −ALTER TABLE "source_recipes" | |
| 2 | −RENAME COLUMN "importer_kind" TO "adapter_key";--> statement-breakpoint | |
| 3 | − | |
| 4 | −ALTER TABLE "control_plane_data_migrations" | |
| 5 | −RENAME TO "control_plane_code_migrations";--> statement-breakpoint | |
| 6 | − | |
| 7 | −UPDATE "source_recipes" | |
| 8 | −SET "adapter_key" = CASE | |
| 9 | − WHEN "adapter_key" = 'graphql_introspection' THEN 'graphql' | |
| 10 | − WHEN "adapter_key" = 'mcp_manifest' THEN 'mcp' | |
| 11 | − ELSE "adapter_key" | |
| 12 | −END | |
| 13 | −WHERE "adapter_key" IN ('graphql_introspection', 'mcp_manifest');--> statement-breakpoint | |
| 14 | − | |
| 15 | −UPDATE "sources" | |
| 16 | −SET "binding_config_json" = jsonb_build_object( | |
| 17 | − 'adapterKey', "kind", | |
| 18 | − 'version', 1, | |
| 19 | − 'payload', | |
| 20 | − CASE | |
| 21 | − WHEN "kind" = 'openapi' THEN jsonb_build_object( | |
| 22 | − 'specUrl', COALESCE(("binding_config_json"::jsonb ->> 'specUrl'), "endpoint"), | |
| 23 | − 'defaultHeaders', COALESCE(("binding_config_json"::jsonb -> 'defaultHeaders'), 'null'::jsonb) | |
| 24 | − ) | |
| 25 | − WHEN "kind" = 'graphql' THEN jsonb_build_object( | |
| 26 | − 'defaultHeaders', COALESCE(("binding_config_json"::jsonb -> 'defaultHeaders'), 'null'::jsonb) | |
| 27 | − ) | |
| 28 | − WHEN "kind" = 'mcp' THEN jsonb_build_object( | |
| 29 | − 'transport', COALESCE(("binding_config_json"::jsonb -> 'transport'), 'null'::jsonb), | |
| 30 | − 'queryParams', COALESCE(("binding_config_json"::jsonb -> 'queryParams'), 'null'::jsonb), | |
| 31 | − 'headers', COALESCE(("binding_config_json"::jsonb -> 'headers'), 'null'::jsonb) | |
| 32 | − ) | |
| 33 | − ELSE '{}'::jsonb | |
| 34 | − END | |
| 35 | −)::text | |
| 36 | −WHERE "binding_config_json" IS NOT NULL | |
| 37 | − AND ( | |
| 38 | − ("binding_config_json"::jsonb -> 'version') IS NULL | |
| 39 | − OR ("binding_config_json"::jsonb -> 'payload') IS NULL | |
| 40 | − );--> statement-breakpoint | |
| 41 | − | |
| 42 | −UPDATE "sources" | |
| 43 | −SET "binding_config_json" = jsonb_set( | |
| 44 | − "binding_config_json"::jsonb, | |
| 45 | − '{adapterKey}', | |
| 46 | − to_jsonb( | |
| 47 | − CASE | |
| 48 | − WHEN "binding_config_json"::jsonb ->> 'adapterKey' = 'graphql_introspection' THEN 'graphql' | |
| 49 | − WHEN "binding_config_json"::jsonb ->> 'adapterKey' = 'mcp_manifest' THEN 'mcp' | |
| 50 | − ELSE "binding_config_json"::jsonb ->> 'adapterKey' | |
| 51 | − END | |
| 52 | − ) | |
| 53 | −)::text | |
| 54 | −WHERE "binding_config_json" IS NOT NULL | |
| 55 | − AND ("binding_config_json"::jsonb ->> 'adapterKey') IN ('graphql_introspection', 'mcp_manifest'); | |
| @@ -1,132 +0,0 @@ | ||
| 1 | −ALTER TABLE "workspace_source_credentials" | |
| 2 | −RENAME TO "workspace_source_auth_artifacts";--> statement-breakpoint | |
| 3 | − | |
| 4 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 5 | −ADD COLUMN "artifact_kind" text;--> statement-breakpoint | |
| 6 | − | |
| 7 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 8 | −ADD COLUMN "config_json" text;--> statement-breakpoint | |
| 9 | − | |
| 10 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 11 | −ADD COLUMN "grant_set_json" text;--> statement-breakpoint | |
| 12 | − | |
| 13 | −UPDATE "workspace_source_auth_artifacts" | |
| 14 | −SET | |
| 15 | − "artifact_kind" = CASE | |
| 16 | − WHEN "auth_kind" = 'bearer' THEN 'static_bearer' | |
| 17 | − WHEN "auth_kind" = 'oauth2' THEN 'static_oauth2' | |
| 18 | − ELSE 'static_placements' | |
| 19 | − END, | |
| 20 | − "config_json" = CASE | |
| 21 | − WHEN "auth_kind" = 'bearer' THEN jsonb_build_object( | |
| 22 | − 'headerName', "auth_header_name", | |
| 23 | − 'prefix', "auth_prefix", | |
| 24 | − 'token', jsonb_build_object( | |
| 25 | − 'providerId', "token_provider_id", | |
| 26 | − 'handle', "token_handle" | |
| 27 | − ) | |
| 28 | − )::text | |
| 29 | − WHEN "auth_kind" = 'oauth2' THEN jsonb_build_object( | |
| 30 | − 'headerName', "auth_header_name", | |
| 31 | − 'prefix', "auth_prefix", | |
| 32 | − 'accessToken', jsonb_build_object( | |
| 33 | − 'providerId', "token_provider_id", | |
| 34 | − 'handle', "token_handle" | |
| 35 | − ), | |
| 36 | − 'refreshToken', | |
| 37 | − CASE | |
| 38 | − WHEN "refresh_token_provider_id" IS NOT NULL AND "refresh_token_handle" IS NOT NULL | |
| 39 | − THEN jsonb_build_object( | |
| 40 | − 'providerId', "refresh_token_provider_id", | |
| 41 | − 'handle', "refresh_token_handle" | |
| 42 | − ) | |
| 43 | − ELSE 'null'::jsonb | |
| 44 | − END | |
| 45 | − )::text | |
| 46 | − ELSE jsonb_build_object('placements', '[]'::jsonb)::text | |
| 47 | − END | |
| 48 | −WHERE "artifact_kind" IS NULL OR "config_json" IS NULL;--> statement-breakpoint | |
| 49 | − | |
| 50 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 51 | −ALTER COLUMN "artifact_kind" SET NOT NULL;--> statement-breakpoint | |
| 52 | − | |
| 53 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 54 | −ALTER COLUMN "config_json" SET NOT NULL;--> statement-breakpoint | |
| 55 | − | |
| 56 | −DROP INDEX IF EXISTS "credentials_workspace_idx";--> statement-breakpoint | |
| 57 | −DROP INDEX IF EXISTS "credentials_workspace_source_actor_idx";--> statement-breakpoint | |
| 58 | −DROP INDEX IF EXISTS "credentials_workspace_source_idx";--> statement-breakpoint | |
| 59 | − | |
| 60 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 61 | −DROP CONSTRAINT IF EXISTS "credentials_auth_kind_check";--> statement-breakpoint | |
| 62 | − | |
| 63 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 64 | −DROP CONSTRAINT IF EXISTS "credentials_slot_check";--> statement-breakpoint | |
| 65 | − | |
| 66 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 67 | −DROP COLUMN "auth_kind";--> statement-breakpoint | |
| 68 | − | |
| 69 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 70 | −DROP COLUMN "auth_header_name";--> statement-breakpoint | |
| 71 | − | |
| 72 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 73 | −DROP COLUMN "auth_prefix";--> statement-breakpoint | |
| 74 | − | |
| 75 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 76 | −DROP COLUMN "token_provider_id";--> statement-breakpoint | |
| 77 | − | |
| 78 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 79 | −DROP COLUMN "token_handle";--> statement-breakpoint | |
| 80 | − | |
| 81 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 82 | −DROP COLUMN "refresh_token_provider_id";--> statement-breakpoint | |
| 83 | − | |
| 84 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 85 | −DROP COLUMN "refresh_token_handle";--> statement-breakpoint | |
| 86 | − | |
| 87 | −ALTER TABLE "workspace_source_auth_artifacts" | |
| 88 | −ADD CONSTRAINT "auth_artifacts_slot_check" | |
| 89 | −CHECK ("slot" in ('runtime', 'import'));--> statement-breakpoint | |
| 90 | − | |
| 91 | −CREATE INDEX "auth_artifacts_workspace_idx" | |
| 92 | −ON "workspace_source_auth_artifacts" ("workspace_id","updated_at","id");--> statement-breakpoint | |
| 93 | − | |
| 94 | −CREATE UNIQUE INDEX "auth_artifacts_workspace_source_actor_idx" | |
| 95 | −ON "workspace_source_auth_artifacts" ("workspace_id","source_id","actor_account_id","slot");--> statement-breakpoint | |
| 96 | − | |
| 97 | −CREATE INDEX "auth_artifacts_workspace_source_idx" | |
| 98 | −ON "workspace_source_auth_artifacts" ("workspace_id","source_id","updated_at","id");--> statement-breakpoint | |
| 99 | − | |
| 100 | −CREATE TABLE "workspace_source_auth_leases" ( | |
| 101 | − "id" text PRIMARY KEY NOT NULL, | |
| 102 | − "auth_artifact_id" text NOT NULL, | |
| 103 | − "workspace_id" text NOT NULL, | |
| 104 | − "source_id" text NOT NULL, | |
| 105 | − "actor_account_id" text, | |
| 106 | − "slot" text NOT NULL, | |
| 107 | − "placements_json" text NOT NULL, | |
| 108 | − "expires_at" bigint, | |
| 109 | − "refresh_after" bigint, | |
| 110 | − "created_at" bigint NOT NULL, | |
| 111 | − "updated_at" bigint NOT NULL | |
| 112 | −);--> statement-breakpoint | |
| 113 | − | |
| 114 | −ALTER TABLE "workspace_source_auth_leases" | |
| 115 | −ADD CONSTRAINT "auth_leases_slot_check" | |
| 116 | −CHECK ("slot" in ('runtime', 'import'));--> statement-breakpoint | |
| 117 | − | |
| 118 | −CREATE UNIQUE INDEX "auth_leases_auth_artifact_idx" | |
| 119 | −ON "workspace_source_auth_leases" ("auth_artifact_id");--> statement-breakpoint | |
| 120 | − | |
| 121 | −CREATE INDEX "auth_leases_workspace_source_idx" | |
| 122 | −ON "workspace_source_auth_leases" ( | |
| 123 | − "workspace_id", | |
| 124 | − "source_id", | |
| 125 | − "actor_account_id", | |
| 126 | − "slot", | |
| 127 | − "updated_at", | |
| 128 | − "id" | |
| 129 | −);--> statement-breakpoint | |
| 130 | − | |
| 131 | −ALTER TABLE "source_auth_sessions" | |
| 132 | −DROP CONSTRAINT IF EXISTS "source_auth_sessions_provider_kind_check";--> statement-breakpoint | |
| @@ -1,40 +0,0 @@ | ||
| 1 | −ALTER TABLE "workspace_source_auth_leases" | |
| 2 | −ADD COLUMN "placements_template_json" text;--> statement-breakpoint | |
| 3 | − | |
| 4 | −UPDATE "workspace_source_auth_leases" | |
| 5 | −SET "placements_template_json" = COALESCE( | |
| 6 | − ( | |
| 7 | − SELECT jsonb_agg( | |
| 8 | − CASE | |
| 9 | − WHEN placement ->> 'location' = 'body' THEN jsonb_build_object( | |
| 10 | − 'location', 'body', | |
| 11 | − 'path', placement ->> 'path', | |
| 12 | − 'parts', jsonb_build_array( | |
| 13 | − jsonb_build_object( | |
| 14 | − 'kind', 'literal', | |
| 15 | − 'value', COALESCE(placement ->> 'value', '') | |
| 16 | − ) | |
| 17 | − ) | |
| 18 | − ) | |
| 19 | − ELSE jsonb_build_object( | |
| 20 | − 'location', placement ->> 'location', | |
| 21 | − 'name', placement ->> 'name', | |
| 22 | − 'parts', jsonb_build_array( | |
| 23 | − jsonb_build_object( | |
| 24 | − 'kind', 'literal', | |
| 25 | − 'value', COALESCE(placement ->> 'value', '') | |
| 26 | − ) | |
| 27 | − ) | |
| 28 | − ) | |
| 29 | − END | |
| 30 | − )::text | |
| 31 | − FROM jsonb_array_elements(COALESCE("placements_json"::jsonb, '[]'::jsonb)) AS placement | |
| 32 | − ), | |
| 33 | − '[]' | |
| 34 | −);--> statement-breakpoint | |
| 35 | − | |
| 36 | −ALTER TABLE "workspace_source_auth_leases" | |
| 37 | −ALTER COLUMN "placements_template_json" SET NOT NULL;--> statement-breakpoint | |
| 38 | − | |
| 39 | −ALTER TABLE "workspace_source_auth_leases" | |
| 40 | −DROP COLUMN "placements_json";--> statement-breakpoint | |
| @@ -1,25 +0,0 @@ | ||
| 1 | −ALTER TABLE "execution_interactions" | |
| 2 | −ADD COLUMN "response_private_json" text;--> statement-breakpoint | |
| 3 | − | |
| 4 | −CREATE TABLE "execution_steps" ( | |
| 5 | − "id" text PRIMARY KEY NOT NULL, | |
| 6 | − "execution_id" text NOT NULL, | |
| 7 | − "sequence" bigint NOT NULL, | |
| 8 | − "kind" text NOT NULL, | |
| 9 | − "status" text NOT NULL, | |
| 10 | − "path" text NOT NULL, | |
| 11 | − "args_json" text NOT NULL, | |
| 12 | − "result_json" text, | |
| 13 | − "error_text" text, | |
| 14 | − "interaction_id" text, | |
| 15 | − "created_at" bigint NOT NULL, | |
| 16 | − "updated_at" bigint NOT NULL, | |
| 17 | − CONSTRAINT "execution_steps_kind_check" CHECK ("execution_steps"."kind" in ('tool_call')), | |
| 18 | − CONSTRAINT "execution_steps_status_check" CHECK ("execution_steps"."status" in ('pending', 'waiting', 'completed', 'failed')) | |
| 19 | −);--> statement-breakpoint | |
| 20 | − | |
| 21 | −CREATE UNIQUE INDEX "execution_steps_execution_sequence_idx" | |
| 22 | −ON "execution_steps" ("execution_id", "sequence");--> statement-breakpoint | |
| 23 | − | |
| 24 | −CREATE INDEX "execution_steps_execution_updated_idx" | |
| 25 | −ON "execution_steps" ("execution_id", "updated_at", "id"); | |
| @@ -12,7 +12,7 @@ import { · 11 unmodified lines | ||
| 12 | 12 | import { |
| 13 | 13 | ENV_SECRET_PROVIDER_ID, |
| 14 | 14 | KEYCHAIN_SECRET_PROVIDER_ID, |
| 15 | − POSTGRES_SECRET_PROVIDER_ID, | |
| 15 | + LOCAL_SECRET_PROVIDER_ID, | |
| 16 | 16 | parseSecretStoreProviderId, |
| 17 | 17 | } from "../../runtime/secret-material-providers"; |
| 18 | 18 | import { ControlPlaneStore } from "../../runtime/store"; |
| @@ -35,8 +35,8 @@ const SECRET_STORE_PROVIDER_ENV = "EXECUTOR_SECRET_STORE_PROVIDER"; · 16 unmodified lines | ||
| 35 | 35 | const getInstanceConfig = (): Effect.Effect<InstanceConfig> => { |
| 36 | 36 | const providers: SecretProvider[] = [ |
| 37 | 37 | { |
| 38 | − id: POSTGRES_SECRET_PROVIDER_ID, | |
| 39 | − name: "Database", | |
| 38 | + id: LOCAL_SECRET_PROVIDER_ID, | |
| 39 | + name: "Local store", | |
| 40 | 40 | canStore: true, |
| 41 | 41 | }, |
| 42 | 42 | ]; |
| @@ -57,7 +57,7 @@ const getInstanceConfig = (): Effect.Effect<InstanceConfig> => { · 14 unmodified lines | ||
| 57 | 57 | |
| 58 | 58 | const defaultStoreProvider = |
| 59 | 59 | parseSecretStoreProviderId(process.env[SECRET_STORE_PROVIDER_ENV]) |
| 60 | − ?? POSTGRES_SECRET_PROVIDER_ID; | |
| 60 | + ?? LOCAL_SECRET_PROVIDER_ID; | |
| 61 | 61 | |
| 62 | 62 | return Effect.succeed({ |
| 63 | 63 | platform: process.platform, |
| @@ -133,7 +133,7 @@ export const ControlPlaneLocalLive = HttpApiBuilder.group( · 69 unmodified lines | ||
| 133 | 133 | return { |
| 134 | 134 | id, |
| 135 | 135 | name, |
| 136 | − providerId: POSTGRES_SECRET_PROVIDER_ID, | |
| 136 | + providerId: LOCAL_SECRET_PROVIDER_ID, | |
| 137 | 137 | purpose, |
| 138 | 138 | createdAt: now, |
| 139 | 139 | updatedAt: now, |
| @@ -1,5 +1,4 @@ | ||
| 1 | 1 | export * from "./schema/index"; |
| 2 | −export * from "./persistence/index"; | |
| 3 | 2 | export * from "./api/index"; |
| 4 | 3 | export * from "./runtime/index"; |
| 5 | 4 | export * from "./client"; |
| @@ -1,70 +0,0 @@ | ||
| 1 | −import * as Effect from "effect/Effect"; | |
| 2 | − | |
| 3 | −import { | |
| 4 | − type ControlPlanePersistenceError, | |
| 5 | − toPersistenceError, | |
| 6 | −} from "./persistence-errors"; | |
| 7 | −import type { DrizzleDb, SqlBackend } from "./sql-runtime"; | |
| 8 | − | |
| 9 | −export type DrizzleSession = Pick<DrizzleDb, "select" | "insert" | "update" | "delete">; | |
| 10 | − | |
| 11 | −export type DrizzleClient = { | |
| 12 | − backend: SqlBackend; | |
| 13 | − db: DrizzleDb; | |
| 14 | − use: <A>( | |
| 15 | − operation: string, | |
| 16 | − run: (db: DrizzleSession) => Promise<A>, | |
| 17 | − ) => Effect.Effect<A, ControlPlanePersistenceError>; | |
| 18 | − useTx: <A>( | |
| 19 | − operation: string, | |
| 20 | − run: (tx: DrizzleSession) => Promise<A>, | |
| 21 | − ) => Effect.Effect<A, ControlPlanePersistenceError>; | |
| 22 | −}; | |
| 23 | − | |
| 24 | −const createSerializer = (backend: SqlBackend) => { | |
| 25 | − let queue = Promise.resolve<void>(undefined); | |
| 26 | − | |
| 27 | − return <A>(run: () => Promise<A>): Promise<A> => { | |
| 28 | − if (backend !== "pglite") { | |
| 29 | − return run(); | |
| 30 | − } | |
| 31 | − | |
| 32 | − const next: Promise<A> = queue.then(run, run); | |
| 33 | − queue = next.then( | |
| 34 | − () => undefined, | |
| 35 | − () => undefined, | |
| 36 | − ); | |
| 37 | − return next; | |
| 38 | − }; | |
| 39 | −}; | |
| 40 | − | |
| 41 | −export const createDrizzleClient = (input: { | |
| 42 | − backend: SqlBackend; | |
| 43 | − db: DrizzleDb; | |
| 44 | −}): DrizzleClient => { | |
| 45 | − const serialize = createSerializer(input.backend); | |
| 46 | − | |
| 47 | − const use = <A>( | |
| 48 | − operation: string, | |
| 49 | − run: (db: DrizzleSession) => Promise<A>, | |
| 50 | − ): Effect.Effect<A, ControlPlanePersistenceError> => | |
| 51 | − Effect.tryPromise({ | |
| 52 | − try: () => serialize(() => run(input.db)), | |
| 53 | − catch: (cause) => toPersistenceError(operation, cause), | |
| 54 | − }); | |
| 55 | − | |
| 56 | − const useTx = <A>( | |
| 57 | − operation: string, | |
| 58 | − run: (tx: DrizzleSession) => Promise<A>, | |
| 59 | − ): Effect.Effect<A, ControlPlanePersistenceError> => | |
| 60 | − use(operation, () => | |
| 61 | − input.db.transaction(async (tx) => run(tx)), | |
| 62 | − ); | |
| 63 | − | |
| 64 | − return { | |
| 65 | − backend: input.backend, | |
| 66 | − db: input.db, | |
| 67 | − use, | |
| 68 | − useTx, | |
| 69 | − }; | |
| 70 | −}; | |
| @@ -1,234 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type McpSourceAuthSessionData, | |
| 3 | − type SourceAuthSession, | |
| 4 | − type StoredSourceRecipeDocumentRecord, | |
| 5 | − type StoredSourceRecord, | |
| 6 | −} from "#schema"; | |
| 7 | −import { | |
| 8 | − JsonObjectSchema, | |
| 9 | − McpSourceAuthSessionDataJsonSchema, | |
| 10 | −} from "#schema"; | |
| 11 | −import * as Either from "effect/Either"; | |
| 12 | −import * as Effect from "effect/Effect"; | |
| 13 | −import * as ParseResult from "effect/ParseResult"; | |
| 14 | −import * as Schema from "effect/Schema"; | |
| 15 | − | |
| 16 | −import { rebuildGraphqlRecipeRevisionContent } from "../runtime/source-adapters/graphql"; | |
| 17 | −import { rebuildOpenApiRecipeRevisionContent } from "../runtime/source-adapters/openapi"; | |
| 18 | −import type { SqlControlPlaneRows } from "./index"; | |
| 19 | − | |
| 20 | −type DataMigration = { | |
| 21 | − id: string; | |
| 22 | − run: (rows: SqlControlPlaneRows) => Effect.Effect<void, Error, never>; | |
| 23 | −}; | |
| 24 | − | |
| 25 | −const LegacyMcpSourceAuthSessionDataSchema = Schema.Struct({ | |
| 26 | − kind: Schema.Literal("mcp_oauth"), | |
| 27 | − endpoint: Schema.String, | |
| 28 | − redirectUri: Schema.String, | |
| 29 | − scope: Schema.NullOr(Schema.String), | |
| 30 | − resourceMetadataUrl: Schema.NullOr(Schema.String), | |
| 31 | − authorizationServerUrl: Schema.NullOr(Schema.String), | |
| 32 | − resourceMetadataJson: Schema.NullOr(Schema.String), | |
| 33 | − authorizationServerMetadataJson: Schema.NullOr(Schema.String), | |
| 34 | − clientInformationJson: Schema.NullOr(Schema.String), | |
| 35 | − codeVerifier: Schema.NullOr(Schema.String), | |
| 36 | − authorizationUrl: Schema.NullOr(Schema.String), | |
| 37 | −}); | |
| 38 | − | |
| 39 | −const decodeLegacyMcpSourceAuthSessionDataJson = Schema.decodeUnknownEither( | |
| 40 | − Schema.parseJson(LegacyMcpSourceAuthSessionDataSchema), | |
| 41 | −); | |
| 42 | − | |
| 43 | −const decodeMcpSourceAuthSessionDataJson = Schema.decodeUnknownEither( | |
| 44 | − McpSourceAuthSessionDataJsonSchema, | |
| 45 | −); | |
| 46 | − | |
| 47 | −const encodeMcpSourceAuthSessionDataJson = Schema.encodeSync( | |
| 48 | − McpSourceAuthSessionDataJsonSchema, | |
| 49 | −); | |
| 50 | − | |
| 51 | −const decodeJsonObjectFromJson = Schema.decodeUnknownEither( | |
| 52 | − Schema.parseJson(JsonObjectSchema), | |
| 53 | −); | |
| 54 | − | |
| 55 | −const decodeLegacyJsonObject = (value: string | null) => { | |
| 56 | − if (value === null) { | |
| 57 | − return null; | |
| 58 | − } | |
| 59 | − | |
| 60 | − const decoded = decodeJsonObjectFromJson(value); | |
| 61 | − return Either.isRight(decoded) ? decoded.right : null; | |
| 62 | −}; | |
| 63 | − | |
| 64 | −const repairLegacyMcpSourceAuthSession = (input: { | |
| 65 | − rows: SqlControlPlaneRows; | |
| 66 | − session: SourceAuthSession; | |
| 67 | −}): Effect.Effect<void, never, never> => { | |
| 68 | − if (input.session.providerKind !== "mcp_oauth") { | |
| 69 | − return Effect.void; | |
| 70 | − } | |
| 71 | − | |
| 72 | − if ( | |
| 73 | − Either.isRight( | |
| 74 | − decodeMcpSourceAuthSessionDataJson(input.session.sessionDataJson), | |
| 75 | − ) | |
| 76 | − ) { | |
| 77 | − return Effect.void; | |
| 78 | − } | |
| 79 | − | |
| 80 | − const legacy = decodeLegacyMcpSourceAuthSessionDataJson( | |
| 81 | − input.session.sessionDataJson, | |
| 82 | − ); | |
| 83 | − if (Either.isLeft(legacy)) { | |
| 84 | − console.warn( | |
| 85 | − `Skipping source auth session data migration for ${input.session.id}: ${ParseResult.TreeFormatter.formatErrorSync(legacy.left)}`, | |
| 86 | − ); | |
| 87 | − return Effect.void; | |
| 88 | − } | |
| 89 | − | |
| 90 | − const repaired: McpSourceAuthSessionData = { | |
| 91 | − kind: "mcp_oauth", | |
| 92 | − endpoint: legacy.right.endpoint, | |
| 93 | − redirectUri: legacy.right.redirectUri, | |
| 94 | − scope: legacy.right.scope, | |
| 95 | − resourceMetadataUrl: legacy.right.resourceMetadataUrl, | |
| 96 | − authorizationServerUrl: legacy.right.authorizationServerUrl, | |
| 97 | − resourceMetadata: decodeLegacyJsonObject(legacy.right.resourceMetadataJson), | |
| 98 | − authorizationServerMetadata: decodeLegacyJsonObject( | |
| 99 | − legacy.right.authorizationServerMetadataJson, | |
| 100 | − ), | |
| 101 | − clientInformation: decodeLegacyJsonObject( | |
| 102 | − legacy.right.clientInformationJson, | |
| 103 | − ), | |
| 104 | − codeVerifier: legacy.right.codeVerifier, | |
| 105 | − authorizationUrl: legacy.right.authorizationUrl, | |
| 106 | − }; | |
| 107 | − | |
| 108 | − return input.rows.sourceAuthSessions | |
| 109 | − .update(input.session.id, { | |
| 110 | − sessionDataJson: encodeMcpSourceAuthSessionDataJson(repaired), | |
| 111 | − }) | |
| 112 | − .pipe(Effect.asVoid, Effect.orDie); | |
| 113 | −}; | |
| 114 | − | |
| 115 | −const migrateLegacySourceAuthSessions: DataMigration = { | |
| 116 | − id: "20260310_repair_legacy_mcp_oauth_sessions", | |
| 117 | − run: (rows) => | |
| 118 | − Effect.gen(function* () { | |
| 119 | − const sourceAuthSessions = yield* rows.sourceAuthSessions.listAll(); | |
| 120 | − yield* Effect.forEach( | |
| 121 | − sourceAuthSessions, | |
| 122 | − (session) => | |
| 123 | − repairLegacyMcpSourceAuthSession({ | |
| 124 | − rows, | |
| 125 | − session, | |
| 126 | − }), | |
| 127 | − { discard: true }, | |
| 128 | − ); | |
| 129 | − }), | |
| 130 | −}; | |
| 131 | − | |
| 132 | −const rebuildPersistedSourceRecipes: DataMigration = { | |
| 133 | − id: "20260310_rebuild_persisted_source_recipes", | |
| 134 | − run: (rows) => | |
| 135 | − Effect.gen(function* () { | |
| 136 | − const sourceRecords = yield* rows.sources.listAll(); | |
| 137 | − if (sourceRecords.length === 0) { | |
| 138 | − return; | |
| 139 | − } | |
| 140 | − | |
| 141 | − const revisionIds = [ | |
| 142 | − ...new Set(sourceRecords.map((sourceRecord) => sourceRecord.recipeRevisionId)), | |
| 143 | − ]; | |
| 144 | − const [revisions, documents] = yield* Effect.all([ | |
| 145 | − rows.sourceRecipeRevisions.listByIds(revisionIds), | |
| 146 | − rows.sourceRecipeDocuments.listByRevisionIds(revisionIds), | |
| 147 | − ]); | |
| 148 | − | |
| 149 | − const revisionById = new Map( | |
| 150 | − revisions.map((revision) => [revision.id, revision]), | |
| 151 | − ); | |
| 152 | − const sourceRecordByRevisionId = new Map<string, StoredSourceRecord>(); | |
| 153 | − for (const sourceRecord of sourceRecords) { | |
| 154 | − if (!sourceRecordByRevisionId.has(sourceRecord.recipeRevisionId)) { | |
| 155 | − sourceRecordByRevisionId.set(sourceRecord.recipeRevisionId, sourceRecord); | |
| 156 | − } | |
| 157 | − } | |
| 158 | − | |
| 159 | − const documentsByRevisionId = new Map<string, StoredSourceRecipeDocumentRecord[]>(); | |
| 160 | − for (const document of documents) { | |
| 161 | − const existing = documentsByRevisionId.get(document.recipeRevisionId) ?? []; | |
| 162 | − existing.push(document); | |
| 163 | − documentsByRevisionId.set(document.recipeRevisionId, existing); | |
| 164 | − } | |
| 165 | − | |
| 166 | − yield* Effect.forEach( | |
| 167 | − revisionIds, | |
| 168 | − (revisionId) => | |
| 169 | − Effect.gen(function* () { | |
| 170 | − const sourceRecord = sourceRecordByRevisionId.get(revisionId); | |
| 171 | − const revision = revisionById.get(revisionId); | |
| 172 | − const documentsForRevision = documentsByRevisionId.get(revisionId) ?? []; | |
| 173 | − if (!sourceRecord || !revision) { | |
| 174 | − return; | |
| 175 | − } | |
| 176 | − | |
| 177 | − if (sourceRecord.kind === "openapi") { | |
| 178 | − if (documentsForRevision.length === 0) { | |
| 179 | − return; | |
| 180 | − } | |
| 181 | − yield* rebuildOpenApiRecipeRevisionContent({ | |
| 182 | − rows, | |
| 183 | − sourceRecord, | |
| 184 | − revision, | |
| 185 | − documents: documentsForRevision, | |
| 186 | − }); | |
| 187 | − return; | |
| 188 | − } | |
| 189 | − | |
| 190 | − if (sourceRecord.kind !== "graphql") { | |
| 191 | − return; | |
| 192 | − } | |
| 193 | − | |
| 194 | − if (documentsForRevision.length === 0) { | |
| 195 | − return; | |
| 196 | − } | |
| 197 | − | |
| 198 | − yield* rebuildGraphqlRecipeRevisionContent({ | |
| 199 | − rows, | |
| 200 | − sourceRecord, | |
| 201 | − revision, | |
| 202 | − documents: documentsForRevision, | |
| 203 | − }); | |
| 204 | − }), | |
| 205 | − { discard: true }, | |
| 206 | − ); | |
| 207 | − }), | |
| 208 | −}; | |
| 209 | − | |
| 210 | −const codeMigrations = [ | |
| 211 | − rebuildPersistedSourceRecipes, | |
| 212 | − migrateLegacySourceAuthSessions, | |
| 213 | −] as const satisfies readonly DataMigration[]; | |
| 214 | − | |
| 215 | −export const runCodeMigrations = ( | |
| 216 | − rows: SqlControlPlaneRows, | |
| 217 | −): Effect.Effect<void, Error, never> => | |
| 218 | − Effect.gen(function* () { | |
| 219 | − const applied = new Set( | |
| 220 | − (yield* rows.codeMigrations.listAll()).map((migration) => migration.id), | |
| 221 | − ); | |
| 222 | − | |
| 223 | − for (const migration of codeMigrations) { | |
| 224 | − if (applied.has(migration.id)) { | |
| 225 | − continue; | |
| 226 | − } | |
| 227 | − | |
| 228 | − yield* migration.run(rows); | |
| 229 | − yield* rows.codeMigrations.upsert({ | |
| 230 | − id: migration.id, | |
| 231 | − appliedAt: Date.now(), | |
| 232 | − }); | |
| 233 | − } | |
| 234 | − }); | |
| @@ -1,182 +0,0 @@ | ||
| 1 | −import * as Context from "effect/Context"; | |
| 2 | −import * as Data from "effect/Data"; | |
| 3 | −import * as Effect from "effect/Effect"; | |
| 4 | −import * as Layer from "effect/Layer"; | |
| 5 | − | |
| 6 | −import { createDrizzleClient, type DrizzleClient } from "./client"; | |
| 7 | −import { | |
| 8 | − createAccountsRepo, | |
| 9 | − createAuthArtifactsRepo, | |
| 10 | − createAuthLeasesRepo, | |
| 11 | − createCodeMigrationsRepo, | |
| 12 | − createExecutionInteractionsRepo, | |
| 13 | − createExecutionStepsRepo, | |
| 14 | − createExecutionsRepo, | |
| 15 | − createSecretMaterialsRepo, | |
| 16 | − createSourceAuthSessionsRepo, | |
| 17 | − createSourceOauthClientsRepo, | |
| 18 | − createSourceRecipeDocumentsRepo, | |
| 19 | − createSourceRecipeSchemaBundlesRepo, | |
| 20 | − createSourceRecipeOperationsRepo, | |
| 21 | − createSourceRecipeRevisionsRepo, | |
| 22 | − createSourceRecipesRepo, | |
| 23 | − createSourcesRepo, | |
| 24 | −} from "./repos"; | |
| 25 | −import { drizzleSchema, tableNames, type DrizzleTables } from "./schema"; | |
| 26 | −import { | |
| 27 | − createSqlRuntime, | |
| 28 | − runMigrations, | |
| 29 | − type CreateSqlRuntimeOptions, | |
| 30 | − type DrizzleDb, | |
| 31 | − type SqlBackend, | |
| 32 | − type SqlRuntime, | |
| 33 | −} from "./sql-runtime"; | |
| 34 | −import { runCodeMigrations } from "./code-migrations"; | |
| 35 | − | |
| 36 | −export { tableNames, type DrizzleTables } from "./schema"; | |
| 37 | −export { | |
| 38 | − ControlPlanePersistenceError, | |
| 39 | − toPersistenceError, | |
| 40 | − type PersistenceErrorKind, | |
| 41 | −} from "./persistence-errors"; | |
| 42 | −export { createDrizzleClient, type DrizzleClient } from "./client"; | |
| 43 | −export { | |
| 44 | − createSqlRuntime, | |
| 45 | − runMigrations, | |
| 46 | − type SqlRuntime, | |
| 47 | − type SqlBackend, | |
| 48 | − type DrizzleDb, | |
| 49 | − type CreateSqlRuntimeOptions, | |
| 50 | −} from "./sql-runtime"; | |
| 51 | − | |
| 52 | −const createRows = (client: DrizzleClient, tables: DrizzleTables = drizzleSchema) => ({ | |
| 53 | − accounts: createAccountsRepo(client, tables), | |
| 54 | − sources: createSourcesRepo(client, tables), | |
| 55 | − sourceRecipes: createSourceRecipesRepo(client, tables), | |
| 56 | − sourceRecipeRevisions: createSourceRecipeRevisionsRepo(client, tables), | |
| 57 | − sourceRecipeDocuments: createSourceRecipeDocumentsRepo(client, tables), | |
| 58 | − sourceRecipeSchemaBundles: createSourceRecipeSchemaBundlesRepo(client, tables), | |
| 59 | − sourceRecipeOperations: createSourceRecipeOperationsRepo(client, tables), | |
| 60 | − codeMigrations: createCodeMigrationsRepo(client, tables), | |
| 61 | − authArtifacts: createAuthArtifactsRepo(client, tables), | |
| 62 | − authLeases: createAuthLeasesRepo(client, tables), | |
| 63 | − sourceOauthClients: createSourceOauthClientsRepo(client, tables), | |
| 64 | − secretMaterials: createSecretMaterialsRepo(client, tables), | |
| 65 | − sourceAuthSessions: createSourceAuthSessionsRepo(client, tables), | |
| 66 | − executions: createExecutionsRepo(client, tables), | |
| 67 | − executionInteractions: createExecutionInteractionsRepo(client, tables), | |
| 68 | − executionSteps: createExecutionStepsRepo(client, tables), | |
| 69 | −}); | |
| 70 | − | |
| 71 | −export type SqlControlPlaneRows = ReturnType<typeof createRows>; | |
| 72 | − | |
| 73 | −export type SqlControlPlanePersistence = { | |
| 74 | − backend: SqlBackend; | |
| 75 | − db: DrizzleDb; | |
| 76 | − rows: SqlControlPlaneRows; | |
| 77 | − close: () => Promise<void>; | |
| 78 | −}; | |
| 79 | − | |
| 80 | −export class SqlControlPlanePersistenceService extends Context.Tag( | |
| 81 | − "#persistence/SqlControlPlanePersistenceService", | |
| 82 | −)<SqlControlPlanePersistenceService, SqlControlPlanePersistence>() {} | |
| 83 | − | |
| 84 | −export class SqlControlPlaneRowsService extends Context.Tag( | |
| 85 | − "#persistence/SqlControlPlaneRowsService", | |
| 86 | −)<SqlControlPlaneRowsService, SqlControlPlaneRows>() {} | |
| 87 | − | |
| 88 | −export class SqlPersistenceBootstrapError extends Data.TaggedError( | |
| 89 | − "SqlPersistenceBootstrapError", | |
| 90 | −)<{ | |
| 91 | − message: string; | |
| 92 | − details: string | null; | |
| 93 | −}> {} | |
| 94 | − | |
| 95 | −const toBootstrapError = (cause: unknown): SqlPersistenceBootstrapError => { | |
| 96 | − const details = cause instanceof Error ? cause.message : String(cause); | |
| 97 | − return new SqlPersistenceBootstrapError({ | |
| 98 | − message: `Failed initializing SQL control-plane persistence: ${details}`, | |
| 99 | − details, | |
| 100 | − }); | |
| 101 | −}; | |
| 102 | − | |
| 103 | −const createRuntimeEffect = (options: CreateSqlRuntimeOptions) => | |
| 104 | − Effect.tryPromise({ | |
| 105 | − try: () => createSqlRuntime(options), | |
| 106 | − catch: toBootstrapError, | |
| 107 | − }); | |
| 108 | − | |
| 109 | −const runMigrationsEffect = ( | |
| 110 | − runtime: SqlRuntime, | |
| 111 | − migrationsFolder: string | undefined, | |
| 112 | −) => | |
| 113 | − Effect.tryPromise({ | |
| 114 | − try: () => runMigrations(runtime, { migrationsFolder }), | |
| 115 | − catch: toBootstrapError, | |
| 116 | − }); | |
| 117 | − | |
| 118 | −const closeRuntimeEffect = (runtime: SqlRuntime) => | |
| 119 | − Effect.tryPromise({ | |
| 120 | − try: () => runtime.close(), | |
| 121 | − catch: (cause) => | |
| 122 | − cause instanceof Error ? cause : new Error(String(cause ?? "unknown close error")), | |
| 123 | − }).pipe(Effect.orDie); | |
| 124 | − | |
| 125 | −export type CreateSqlControlPlanePersistenceOptions = | |
| 126 | − & CreateSqlRuntimeOptions | |
| 127 | − & { | |
| 128 | − runCodeMigrations?: boolean; | |
| 129 | − }; | |
| 130 | − | |
| 131 | −export const createSqlControlPlanePersistence = ( | |
| 132 | − options: CreateSqlControlPlanePersistenceOptions, | |
| 133 | −): Effect.Effect<SqlControlPlanePersistence, SqlPersistenceBootstrapError> => | |
| 134 | − Effect.flatMap(createRuntimeEffect(options), (runtime) => | |
| 135 | − runMigrationsEffect(runtime, options.migrationsFolder).pipe( | |
| 136 | − Effect.map(() => { | |
| 137 | − const client = createDrizzleClient({ | |
| 138 | − backend: runtime.backend, | |
| 139 | − db: runtime.db, | |
| 140 | − }); | |
| 141 | − const rows = createRows(client); | |
| 142 | − | |
| 143 | − return { | |
| 144 | − backend: runtime.backend, | |
| 145 | − db: runtime.db, | |
| 146 | − rows, | |
| 147 | − close: () => runtime.close(), | |
| 148 | − } satisfies SqlControlPlanePersistence; | |
| 149 | − }), | |
| 150 | − Effect.flatMap((persistence) => | |
| 151 | − options.runCodeMigrations === false | |
| 152 | − ? Effect.succeed(persistence) | |
| 153 | − : runCodeMigrations(persistence.rows).pipe( | |
| 154 | − Effect.mapError(toBootstrapError), | |
| 155 | − Effect.map(() => persistence), | |
| 156 | − )), | |
| 157 | − Effect.catchAll((error) => | |
| 158 | − closeRuntimeEffect(runtime).pipe( | |
| 159 | − Effect.zipRight(Effect.fail(error)), | |
| 160 | − )), | |
| 161 | − )); | |
| 162 | − | |
| 163 | −export const SqlControlPlanePersistenceLive = ( | |
| 164 | − options: CreateSqlControlPlanePersistenceOptions, | |
| 165 | −) => | |
| 166 | − Layer.scoped( | |
| 167 | − SqlControlPlanePersistenceService, | |
| 168 | − Effect.acquireRelease( | |
| 169 | − createSqlControlPlanePersistence(options), | |
| 170 | − (persistence) => | |
| 171 | − Effect.tryPromise({ | |
| 172 | − try: () => persistence.close(), | |
| 173 | − catch: (cause) => | |
| 174 | − cause instanceof Error ? cause : new Error(String(cause ?? "unknown close error")), | |
| 175 | − }).pipe(Effect.orDie), | |
| 176 | − ), | |
| 177 | − ); | |
| 178 | − | |
| 179 | −export const SqlControlPlaneRowsLive = Layer.effect( | |
| 180 | − SqlControlPlaneRowsService, | |
| 181 | − Effect.map(SqlControlPlanePersistenceService, (persistence) => persistence.rows), | |
| 182 | −); | |
| @@ -1,92 +0,0 @@ | ||
| 1 | −import * as Data from "effect/Data"; | |
| 2 | − | |
| 3 | −export type PersistenceErrorKind = | |
| 4 | − | "unique_violation" | |
| 5 | − | "not_null_violation" | |
| 6 | − | "check_violation" | |
| 7 | − | "foreign_key_violation" | |
| 8 | − | "serialization_failure" | |
| 9 | − | "unknown"; | |
| 10 | − | |
| 11 | −type ErrorMetadata = { | |
| 12 | − code: string | null; | |
| 13 | − constraint: string | null; | |
| 14 | − table: string | null; | |
| 15 | − details: string | null; | |
| 16 | − message: string; | |
| 17 | − kind: PersistenceErrorKind; | |
| 18 | −}; | |
| 19 | − | |
| 20 | −export class ControlPlanePersistenceError extends Data.TaggedError( | |
| 21 | − "ControlPlanePersistenceError", | |
| 22 | −)<{ | |
| 23 | − operation: string; | |
| 24 | − message: string; | |
| 25 | − details: string | null; | |
| 26 | − code: string | null; | |
| 27 | − constraint: string | null; | |
| 28 | − table: string | null; | |
| 29 | − kind: PersistenceErrorKind; | |
| 30 | − cause: unknown; | |
| 31 | −}> {} | |
| 32 | − | |
| 33 | −const asRecord = (value: unknown): Record<string, unknown> | null => | |
| 34 | − typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null; | |
| 35 | − | |
| 36 | −const readString = (value: unknown): string | null => | |
| 37 | − typeof value === "string" && value.length > 0 ? value : null; | |
| 38 | − | |
| 39 | −const classifyKind = (code: string | null): PersistenceErrorKind => { | |
| 40 | − switch (code) { | |
| 41 | − case "23505": | |
| 42 | − return "unique_violation"; | |
| 43 | − case "23502": | |
| 44 | − return "not_null_violation"; | |
| 45 | − case "23514": | |
| 46 | − return "check_violation"; | |
| 47 | − case "23503": | |
| 48 | − return "foreign_key_violation"; | |
| 49 | − case "40001": | |
| 50 | − return "serialization_failure"; | |
| 51 | − default: | |
| 52 | − return "unknown"; | |
| 53 | − } | |
| 54 | −}; | |
| 55 | − | |
| 56 | −const toErrorMetadata = (cause: unknown): ErrorMetadata => { | |
| 57 | − const record = asRecord(cause); | |
| 58 | − const code = readString(record?.code); | |
| 59 | − const constraint = | |
| 60 | − readString(record?.constraint) | |
| 61 | − ?? readString(record?.constraint_name) | |
| 62 | − ?? readString(record?.constraintName); | |
| 63 | − const table = readString(record?.table) ?? readString(record?.table_name); | |
| 64 | − const details = readString(record?.detail) ?? readString(record?.details); | |
| 65 | − const message = cause instanceof Error ? cause.message : String(cause); | |
| 66 | − | |
| 67 | − return { | |
| 68 | − code, | |
| 69 | − constraint, | |
| 70 | − table, | |
| 71 | − details, | |
| 72 | − message, | |
| 73 | − kind: classifyKind(code), | |
| 74 | − }; | |
| 75 | −}; | |
| 76 | − | |
| 77 | −export const toPersistenceError = ( | |
| 78 | − operation: string, | |
| 79 | − cause: unknown, | |
| 80 | −): ControlPlanePersistenceError => { | |
| 81 | − const metadata = toErrorMetadata(cause); | |
| 82 | − return new ControlPlanePersistenceError({ | |
| 83 | − operation, | |
| 84 | − message: `Control-plane persistence failed during ${operation}`, | |
| 85 | − details: metadata.details ?? metadata.message, | |
| 86 | − code: metadata.code, | |
| 87 | − constraint: metadata.constraint, | |
| 88 | − table: metadata.table, | |
| 89 | − kind: metadata.kind, | |
| 90 | − cause, | |
| 91 | − }); | |
| 92 | −}; | |
| @@ -1,915 +0,0 @@ | ||
| 1 | −import { cpSync, mkdtempSync, readdirSync, rmSync } from "node:fs"; | |
| 2 | −import { tmpdir } from "node:os"; | |
| 3 | −import path from "node:path"; | |
| 4 | −import { fileURLToPath } from "node:url"; | |
| 5 | − | |
| 6 | −import { describe, expect, it } from "@effect/vitest"; | |
| 7 | −import { | |
| 8 | − AccountIdSchema, | |
| 9 | − McpSourceAuthSessionDataJsonSchema, | |
| 10 | − SourceAuthSessionIdSchema, | |
| 11 | − decodeAuthLeasePlacementTemplates, | |
| 12 | − SourceIdSchema, | |
| 13 | − SourceRecipeIdSchema, | |
| 14 | − SourceRecipeRevisionIdSchema, | |
| 15 | − WorkspaceIdSchema, | |
| 16 | −} from "#schema"; | |
| 17 | −import * as Effect from "effect/Effect"; | |
| 18 | −import * as Option from "effect/Option"; | |
| 19 | −import * as Schema from "effect/Schema"; | |
| 20 | −import * as Scope from "effect/Scope"; | |
| 21 | −import { buildSchema, getIntrospectionQuery, graphqlSync } from "graphql"; | |
| 22 | −import { sql } from "drizzle-orm"; | |
| 23 | − | |
| 24 | −import { | |
| 25 | − createSqlControlPlanePersistence, | |
| 26 | − type SqlControlPlanePersistence, | |
| 27 | −} from "./index"; | |
| 28 | −import { runCodeMigrations } from "./code-migrations"; | |
| 29 | − | |
| 30 | −const openApiBindingConfigJson = (specUrl: string): string => | |
| 31 | − JSON.stringify({ | |
| 32 | − adapterKey: "openapi", | |
| 33 | − version: 1, | |
| 34 | − payload: { | |
| 35 | − specUrl, | |
| 36 | − defaultHeaders: null, | |
| 37 | − }, | |
| 38 | − }); | |
| 39 | − | |
| 40 | −const graphqlBindingConfigJson = (): string => | |
| 41 | − JSON.stringify({ | |
| 42 | − adapterKey: "graphql", | |
| 43 | − version: 1, | |
| 44 | − payload: { | |
| 45 | − defaultHeaders: null, | |
| 46 | − }, | |
| 47 | − }); | |
| 48 | − | |
| 49 | −const baseRevisionRecord = (input: { | |
| 50 | − id: ReturnType<typeof SourceRecipeRevisionIdSchema.make>; | |
| 51 | − recipeId: ReturnType<typeof SourceRecipeIdSchema.make>; | |
| 52 | − revisionNumber: number; | |
| 53 | − sourceConfigJson: string; | |
| 54 | − manifestJson?: string | null; | |
| 55 | − manifestHash?: string | null; | |
| 56 | − materializationHash?: string | null; | |
| 57 | − createdAt: number; | |
| 58 | − updatedAt: number; | |
| 59 | −}) => ({ | |
| 60 | − id: input.id, | |
| 61 | − recipeId: input.recipeId, | |
| 62 | − revisionNumber: input.revisionNumber, | |
| 63 | − sourceConfigJson: input.sourceConfigJson, | |
| 64 | − manifestJson: input.manifestJson ?? null, | |
| 65 | − manifestHash: input.manifestHash ?? null, | |
| 66 | − materializationHash: input.materializationHash ?? null, | |
| 67 | − createdAt: input.createdAt, | |
| 68 | − updatedAt: input.updatedAt, | |
| 69 | −}); | |
| 70 | − | |
| 71 | −const drizzleDir = path.resolve( | |
| 72 | − path.dirname(fileURLToPath(import.meta.url)), | |
| 73 | − "../../drizzle", | |
| 74 | −); | |
| 75 | − | |
| 76 | −const copyMigrationsBefore = (cutoffDirectoryName: string): string => { | |
| 77 | − const targetDir = mkdtempSync(path.join(tmpdir(), "executor-cp-migrations-")); | |
| 78 | − for (const directoryName of readdirSync(drizzleDir).sort()) { | |
| 79 | − if (directoryName >= cutoffDirectoryName) { | |
| 80 | − continue; | |
| 81 | − } | |
| 82 | − | |
| 83 | − cpSync( | |
| 84 | − path.join(drizzleDir, directoryName), | |
| 85 | − path.join(targetDir, directoryName), | |
| 86 | − { recursive: true }, | |
| 87 | − ); | |
| 88 | − } | |
| 89 | − | |
| 90 | − return targetDir; | |
| 91 | −}; | |
| 92 | − | |
| 93 | −const makePersistence: Effect.Effect< | |
| 94 | − SqlControlPlanePersistence, | |
| 95 | − unknown, | |
| 96 | − Scope.Scope | |
| 97 | −> = Effect.acquireRelease( | |
| 98 | − createSqlControlPlanePersistence({ | |
| 99 | − localDataDir: ":memory:", | |
| 100 | − }), | |
| 101 | − (persistence) => | |
| 102 | − Effect.tryPromise({ | |
| 103 | − try: () => persistence.close(), | |
| 104 | − catch: (cause) => | |
| 105 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 106 | − }).pipe(Effect.orDie), | |
| 107 | −); | |
| 108 | − | |
| 109 | −const seedMigratedSourceRecipe = (input: { | |
| 110 | − persistence: SqlControlPlanePersistence; | |
| 111 | − kind: "openapi" | "graphql"; | |
| 112 | − workspaceId: ReturnType<typeof WorkspaceIdSchema.make>; | |
| 113 | − sourceId: ReturnType<typeof SourceIdSchema.make>; | |
| 114 | − documentText: string; | |
| 115 | −}): Effect.Effect< | |
| 116 | − { | |
| 117 | − recipeRevisionId: ReturnType<typeof SourceRecipeRevisionIdSchema.make>; | |
| 118 | − }, | |
| 119 | − unknown, | |
| 120 | − never | |
| 121 | −> => | |
| 122 | − Effect.gen(function* () { | |
| 123 | − const now = Date.now(); | |
| 124 | − const accountId = AccountIdSchema.make(`acc_${input.sourceId}`); | |
| 125 | − const recipeId = SourceRecipeIdSchema.make(`src_recipe_${input.sourceId}`); | |
| 126 | − const recipeRevisionId = SourceRecipeRevisionIdSchema.make( | |
| 127 | − `src_recipe_rev_${input.sourceId}`, | |
| 128 | − ); | |
| 129 | − | |
| 130 | − yield* input.persistence.rows.sourceRecipes.upsert({ | |
| 131 | − id: recipeId, | |
| 132 | − kind: "http_api", | |
| 133 | − adapterKey: | |
| 134 | − input.kind === "openapi" ? "openapi" : "graphql", | |
| 135 | − providerKey: | |
| 136 | − input.kind === "openapi" ? "generic_http" : "generic_graphql", | |
| 137 | − name: input.kind === "openapi" ? "GitHub" : "GraphQL Demo", | |
| 138 | − summary: null, | |
| 139 | − visibility: "workspace", | |
| 140 | − latestRevisionId: recipeRevisionId, | |
| 141 | − createdAt: now, | |
| 142 | − updatedAt: now, | |
| 143 | − }); | |
| 144 | − yield* input.persistence.rows.sourceRecipeRevisions.upsert(baseRevisionRecord({ | |
| 145 | − id: recipeRevisionId, | |
| 146 | − recipeId, | |
| 147 | − revisionNumber: 1, | |
| 148 | − sourceConfigJson: JSON.stringify( | |
| 149 | − input.kind === "openapi" | |
| 150 | − ? { | |
| 151 | − kind: "openapi", | |
| 152 | − endpoint: "https://api.example.com", | |
| 153 | − specUrl: "https://api.example.com/openapi.json", | |
| 154 | − defaultHeaders: null, | |
| 155 | − } | |
| 156 | − : { | |
| 157 | − kind: "graphql", | |
| 158 | − endpoint: "https://api.example.com/graphql", | |
| 159 | − defaultHeaders: null, | |
| 160 | − }, | |
| 161 | − ), | |
| 162 | − manifestJson: null, | |
| 163 | − manifestHash: null, | |
| 164 | − materializationHash: null, | |
| 165 | − createdAt: now, | |
| 166 | − updatedAt: now, | |
| 167 | − })); | |
| 168 | − yield* input.persistence.rows.sources.insert({ | |
| 169 | − id: input.sourceId, | |
| 170 | − workspaceId: input.workspaceId, | |
| 171 | − recipeId, | |
| 172 | − recipeRevisionId, | |
| 173 | − name: input.kind === "openapi" ? "GitHub" : "GraphQL Demo", | |
| 174 | − kind: input.kind, | |
| 175 | − endpoint: | |
| 176 | − input.kind === "openapi" | |
| 177 | − ? "https://api.example.com" | |
| 178 | − : "https://api.example.com/graphql", | |
| 179 | − status: "connected", | |
| 180 | − enabled: true, | |
| 181 | − namespace: input.kind === "openapi" ? "github" : "graphql", | |
| 182 | − importAuthPolicy: "reuse_runtime", | |
| 183 | − bindingConfigJson: | |
| 184 | − input.kind === "openapi" | |
| 185 | − ? openApiBindingConfigJson("https://api.example.com/openapi.json") | |
| 186 | − : graphqlBindingConfigJson(), | |
| 187 | − sourceHash: null, | |
| 188 | − lastError: null, | |
| 189 | − createdAt: now, | |
| 190 | − updatedAt: now, | |
| 191 | − }); | |
| 192 | − yield* input.persistence.rows.sourceRecipeDocuments.replaceForRevision({ | |
| 193 | − recipeRevisionId, | |
| 194 | − documents: [ | |
| 195 | − { | |
| 196 | − id: `src_recipe_doc_${input.sourceId}`, | |
| 197 | − recipeRevisionId, | |
| 198 | − documentKind: | |
| 199 | − input.kind === "openapi" ? "openapi" : "graphql_introspection", | |
| 200 | − documentKey: | |
| 201 | − input.kind === "openapi" | |
| 202 | − ? "https://api.example.com/openapi.json" | |
| 203 | − : "https://api.example.com/graphql", | |
| 204 | − contentText: input.documentText, | |
| 205 | − contentHash: `hash_${input.sourceId}`, | |
| 206 | − fetchedAt: now, | |
| 207 | − createdAt: now, | |
| 208 | − updatedAt: now, | |
| 209 | − }, | |
| 210 | − ], | |
| 211 | − }); | |
| 212 | − | |
| 213 | − return { recipeRevisionId }; | |
| 214 | − }); | |
| 215 | − | |
| 216 | −describe("code-migrations", () => { | |
| 217 | − it.scoped("repairs migrated OpenAPI recipes from stored documents", () => | |
| 218 | − Effect.gen(function* () { | |
| 219 | − const persistence = yield* makePersistence; | |
| 220 | − const workspaceId = WorkspaceIdSchema.make("ws_post_migration_openapi"); | |
| 221 | − const sourceId = SourceIdSchema.make("src_post_migration_openapi"); | |
| 222 | − yield* persistence.rows.codeMigrations.clearAll(); | |
| 223 | − | |
| 224 | − const openApiDocument = JSON.stringify({ | |
| 225 | − openapi: "3.0.3", | |
| 226 | − info: { | |
| 227 | − title: "GitHub", | |
| 228 | − version: "1.0.0", | |
| 229 | − }, | |
| 230 | − paths: { | |
| 231 | − "/repos/{owner}/{repo}": { | |
| 232 | − get: { | |
| 233 | − operationId: "repos.getRepo", | |
| 234 | − parameters: [ | |
| 235 | − { | |
| 236 | − name: "owner", | |
| 237 | − in: "path", | |
| 238 | − required: true, | |
| 239 | − schema: { type: "string" }, | |
| 240 | − }, | |
| 241 | − { | |
| 242 | − name: "repo", | |
| 243 | − in: "path", | |
| 244 | − required: true, | |
| 245 | − schema: { type: "string" }, | |
| 246 | − }, | |
| 247 | − ], | |
| 248 | − responses: { | |
| 249 | − 200: { | |
| 250 | − description: "ok", | |
| 251 | − content: { | |
| 252 | − "application/json": { | |
| 253 | − schema: { | |
| 254 | − $ref: "#/components/schemas/Repository", | |
| 255 | − }, | |
| 256 | − }, | |
| 257 | − }, | |
| 258 | − }, | |
| 259 | − }, | |
| 260 | − }, | |
| 261 | − }, | |
| 262 | − }, | |
| 263 | − components: { | |
| 264 | − schemas: { | |
| 265 | − Repository: { | |
| 266 | − type: "object", | |
| 267 | − properties: { | |
| 268 | − full_name: { type: "string" }, | |
| 269 | − }, | |
| 270 | − required: ["full_name"], | |
| 271 | − }, | |
| 272 | − }, | |
| 273 | − }, | |
| 274 | − }); | |
| 275 | − | |
| 276 | − const { recipeRevisionId } = yield* seedMigratedSourceRecipe({ | |
| 277 | − persistence, | |
| 278 | − kind: "openapi", | |
| 279 | − workspaceId, | |
| 280 | − sourceId, | |
| 281 | − documentText: openApiDocument, | |
| 282 | − }); | |
| 283 | − | |
| 284 | − yield* runCodeMigrations(persistence.rows); | |
| 285 | − | |
| 286 | − const revision = | |
| 287 | − yield* persistence.rows.sourceRecipeRevisions.getById(recipeRevisionId); | |
| 288 | − expect(Option.isSome(revision)).toBe(true); | |
| 289 | − expect(revision.pipe(Option.getOrNull)?.manifestJson).not.toBeNull(); | |
| 290 | − expect( | |
| 291 | − (yield* persistence.rows.sourceRecipeSchemaBundles.listByRevisionId( | |
| 292 | − recipeRevisionId, | |
| 293 | − )).length, | |
| 294 | − ).toBe(1); | |
| 295 | − expect( | |
| 296 | − (yield* persistence.rows.sourceRecipeOperations.listByRevisionId( | |
| 297 | − recipeRevisionId, | |
| 298 | − )).length, | |
| 299 | − ).toBeGreaterThan(0); | |
| 300 | − }), | |
| 301 | − 60_000, | |
| 302 | − ); | |
| 303 | − | |
| 304 | − it.scoped("repairs migrated GraphQL recipes from stored documents", () => | |
| 305 | − Effect.gen(function* () { | |
| 306 | − const persistence = yield* makePersistence; | |
| 307 | − const workspaceId = WorkspaceIdSchema.make("ws_post_migration_graphql"); | |
| 308 | − const sourceId = SourceIdSchema.make("src_post_migration_graphql"); | |
| 309 | − yield* persistence.rows.codeMigrations.clearAll(); | |
| 310 | − const schema = buildSchema(` | |
| 311 | − type Query { | |
| 312 | − viewer: User! | |
| 313 | − } | |
| 314 | − | |
| 315 | − type Mutation { | |
| 316 | − createIssue(title: String!): Issue! | |
| 317 | − } | |
| 318 | − | |
| 319 | − type User { | |
| 320 | − login: String! | |
| 321 | − } | |
| 322 | − | |
| 323 | − type Issue { | |
| 324 | − id: ID! | |
| 325 | − title: String! | |
| 326 | − } | |
| 327 | − `); | |
| 328 | − const graphqlDocument = JSON.stringify( | |
| 329 | − graphqlSync({ | |
| 330 | − schema, | |
| 331 | − source: getIntrospectionQuery(), | |
| 332 | − }), | |
| 333 | − ); | |
| 334 | − | |
| 335 | − const { recipeRevisionId } = yield* seedMigratedSourceRecipe({ | |
| 336 | − persistence, | |
| 337 | − kind: "graphql", | |
| 338 | − workspaceId, | |
| 339 | − sourceId, | |
| 340 | − documentText: graphqlDocument, | |
| 341 | − }); | |
| 342 | − | |
| 343 | − yield* runCodeMigrations(persistence.rows); | |
| 344 | − | |
| 345 | − const revision = | |
| 346 | − yield* persistence.rows.sourceRecipeRevisions.getById(recipeRevisionId); | |
| 347 | − expect(Option.isSome(revision)).toBe(true); | |
| 348 | − expect(revision.pipe(Option.getOrNull)?.manifestJson).not.toBeNull(); | |
| 349 | − expect( | |
| 350 | − (yield* persistence.rows.sourceRecipeSchemaBundles.listByRevisionId( | |
| 351 | − recipeRevisionId, | |
| 352 | − )).length, | |
| 353 | − ).toBe(1); | |
| 354 | − expect( | |
| 355 | − (yield* persistence.rows.sourceRecipeOperations.listByRevisionId( | |
| 356 | − recipeRevisionId, | |
| 357 | − )).length, | |
| 358 | − ).toBeGreaterThan(0); | |
| 359 | − }), | |
| 360 | − 60_000, | |
| 361 | − ); | |
| 362 | − | |
| 363 | − it.scoped("repairs legacy MCP source auth session payloads", () => | |
| 364 | − Effect.gen(function* () { | |
| 365 | − const persistence = yield* makePersistence; | |
| 366 | − const workspaceId = WorkspaceIdSchema.make("ws_post_migration_session"); | |
| 367 | − const sourceId = SourceIdSchema.make("src_post_migration_session"); | |
| 368 | − yield* persistence.rows.codeMigrations.clearAll(); | |
| 369 | − const sessionId = SourceAuthSessionIdSchema.make( | |
| 370 | − "src_auth_post_migration_session", | |
| 371 | − ); | |
| 372 | − const now = Date.now(); | |
| 373 | − | |
| 374 | − yield* persistence.rows.sourceAuthSessions.insert({ | |
| 375 | − id: sessionId, | |
| 376 | − workspaceId, | |
| 377 | − sourceId, | |
| 378 | − actorAccountId: null, | |
| 379 | − executionId: null, | |
| 380 | − interactionId: null, | |
| 381 | − providerKind: "mcp_oauth", | |
| 382 | − credentialSlot: "runtime", | |
| 383 | − status: "pending", | |
| 384 | − state: "state_post_migration_session", | |
| 385 | − sessionDataJson: JSON.stringify({ | |
| 386 | − kind: "mcp_oauth", | |
| 387 | − endpoint: "https://api.github.com", | |
| 388 | − redirectUri: "http://127.0.0.1/callback", | |
| 389 | − scope: null, | |
| 390 | − resourceMetadataUrl: | |
| 391 | − "https://api.github.com/.well-known/oauth-protected-resource", | |
| 392 | − authorizationServerUrl: "https://github.com/login/oauth", | |
| 393 | − resourceMetadataJson: '{"issuer":"https://api.github.com"}', | |
| 394 | − authorizationServerMetadataJson: | |
| 395 | − '{"token_endpoint":"https://github.com/login/oauth/access_token"}', | |
| 396 | − clientInformationJson: '{"client_id":"abc123"}', | |
| 397 | − codeVerifier: "verifier", | |
| 398 | − authorizationUrl: "https://github.com/login/oauth/authorize", | |
| 399 | − }), | |
| 400 | − errorText: null, | |
| 401 | − completedAt: null, | |
| 402 | − createdAt: now, | |
| 403 | − updatedAt: now, | |
| 404 | − }); | |
| 405 | − | |
| 406 | − yield* runCodeMigrations(persistence.rows); | |
| 407 | − | |
| 408 | − const session = | |
| 409 | − yield* persistence.rows.sourceAuthSessions.getById(sessionId); | |
| 410 | − expect(Option.isSome(session)).toBe(true); | |
| 411 | − const repairedSession = session.pipe(Option.getOrNull); | |
| 412 | − expect(repairedSession).not.toBeNull(); | |
| 413 | − const repaired = Schema.decodeUnknownSync( | |
| 414 | − McpSourceAuthSessionDataJsonSchema, | |
| 415 | − )(repairedSession!.sessionDataJson); | |
| 416 | − expect(repaired.resourceMetadata).toEqual({ | |
| 417 | − issuer: "https://api.github.com", | |
| 418 | − }); | |
| 419 | − expect(repaired.authorizationServerMetadata).toEqual({ | |
| 420 | − token_endpoint: "https://github.com/login/oauth/access_token", | |
| 421 | − }); | |
| 422 | − expect(repaired.clientInformation).toEqual({ | |
| 423 | − client_id: "abc123", | |
| 424 | − }); | |
| 425 | − }), | |
| 426 | − ); | |
| 427 | − | |
| 428 | − it("upgrades legacy source binding columns into adapter-owned binding config", async () => { | |
| 429 | − const localDataDir = mkdtempSync(path.join(tmpdir(), "executor-cp-db-")); | |
| 430 | − const legacyMigrationsDir = copyMigrationsBefore( | |
| 431 | − "20260311010000_source_binding_configs", | |
| 432 | − ); | |
| 433 | − const workspaceId = WorkspaceIdSchema.make("ws_legacy_source_bindings"); | |
| 434 | − const accountId = AccountIdSchema.make("acc_legacy_source_bindings"); | |
| 435 | − const now = Date.now(); | |
| 436 | − let legacyPersistence: SqlControlPlanePersistence | null = null; | |
| 437 | − let upgradedPersistence: SqlControlPlanePersistence | null = null; | |
| 438 | − | |
| 439 | − try { | |
| 440 | − legacyPersistence = await Effect.runPromise( | |
| 441 | − createSqlControlPlanePersistence({ | |
| 442 | − localDataDir, | |
| 443 | − migrationsFolder: legacyMigrationsDir, | |
| 444 | − runCodeMigrations: false, | |
| 445 | − }), | |
| 446 | − ); | |
| 447 | − | |
| 448 | − const seedRecipe = async (input: { | |
| 449 | − sourceId: string; | |
| 450 | − kind: "openapi" | "graphql" | "mcp"; | |
| 451 | − adapterKey: string; | |
| 452 | − providerKey: string; | |
| 453 | − name: string; | |
| 454 | − endpoint: string; | |
| 455 | − sourceConfigJson: string; | |
| 456 | − }) => { | |
| 457 | − const recipeId = SourceRecipeIdSchema.make(`src_recipe_${input.sourceId}`); | |
| 458 | − const recipeRevisionId = SourceRecipeRevisionIdSchema.make( | |
| 459 | − `src_recipe_rev_${input.sourceId}`, | |
| 460 | − ); | |
| 461 | − | |
| 462 | − await legacyPersistence!.db.execute(sql` | |
| 463 | − INSERT INTO "source_recipes" ( | |
| 464 | − "id", | |
| 465 | − "kind", | |
| 466 | − "importer_kind", | |
| 467 | − "provider_key", | |
| 468 | − "name", | |
| 469 | − "summary", | |
| 470 | − "visibility", | |
| 471 | − "latest_revision_id", | |
| 472 | − "created_at", | |
| 473 | − "updated_at" | |
| 474 | − ) VALUES ( | |
| 475 | − ${recipeId}, | |
| 476 | − ${input.kind === "mcp" ? "mcp" : "http_api"}, | |
| 477 | − ${input.adapterKey}, | |
| 478 | − ${input.providerKey}, | |
| 479 | − ${input.name}, | |
| 480 | − NULL, | |
| 481 | − 'workspace', | |
| 482 | − ${recipeRevisionId}, | |
| 483 | − ${now}, | |
| 484 | − ${now} | |
| 485 | − ) | |
| 486 | − `); | |
| 487 | − await legacyPersistence!.db.execute(sql` | |
| 488 | − INSERT INTO "source_recipe_revisions" ( | |
| 489 | − "id", | |
| 490 | − "recipe_id", | |
| 491 | − "revision_number", | |
| 492 | − "source_config_json", | |
| 493 | − "manifest_json", | |
| 494 | − "manifest_hash", | |
| 495 | − "created_at", | |
| 496 | − "updated_at" | |
| 497 | − ) VALUES ( | |
| 498 | − ${recipeRevisionId}, | |
| 499 | − ${recipeId}, | |
| 500 | − 1, | |
| 501 | − ${input.sourceConfigJson}, | |
| 502 | − NULL, | |
| 503 | − NULL, | |
| 504 | − ${now}, | |
| 505 | − ${now} | |
| 506 | − ) | |
| 507 | − `); | |
| 508 | − | |
| 509 | − return { recipeId, recipeRevisionId }; | |
| 510 | − }; | |
| 511 | − | |
| 512 | − const openapi = await seedRecipe({ | |
| 513 | − sourceId: "src_legacy_openapi", | |
| 514 | − kind: "openapi", | |
| 515 | − adapterKey: "openapi", | |
| 516 | − providerKey: "generic_http", | |
| 517 | − name: "Legacy OpenAPI", | |
| 518 | − endpoint: "https://api.example.com", | |
| 519 | − sourceConfigJson: JSON.stringify({ | |
| 520 | − kind: "openapi", | |
| 521 | − endpoint: "https://api.example.com", | |
| 522 | − specUrl: "https://api.example.com/openapi.json", | |
| 523 | − defaultHeaders: { accept: "application/json" }, | |
| 524 | − }), | |
| 525 | − }); | |
| 526 | − const graphql = await seedRecipe({ | |
| 527 | − sourceId: "src_legacy_graphql", | |
| 528 | − kind: "graphql", | |
| 529 | − adapterKey: "graphql_introspection", | |
| 530 | − providerKey: "generic_graphql", | |
| 531 | − name: "Legacy GraphQL", | |
| 532 | − endpoint: "https://api.example.com/graphql", | |
| 533 | − sourceConfigJson: JSON.stringify({ | |
| 534 | − kind: "graphql", | |
| 535 | − endpoint: "https://api.example.com/graphql", | |
| 536 | − defaultHeaders: { accept: "application/json" }, | |
| 537 | − }), | |
| 538 | − }); | |
| 539 | − const mcp = await seedRecipe({ | |
| 540 | − sourceId: "src_legacy_mcp", | |
| 541 | − kind: "mcp", | |
| 542 | − adapterKey: "mcp_manifest", | |
| 543 | − providerKey: "generic_mcp", | |
| 544 | − name: "Legacy MCP", | |
| 545 | − endpoint: "https://api.example.com/mcp", | |
| 546 | − sourceConfigJson: JSON.stringify({ | |
| 547 | − kind: "mcp", | |
| 548 | − endpoint: "https://api.example.com/mcp", | |
| 549 | − transport: "streamable-http", | |
| 550 | − queryParams: { tenant: "acme" }, | |
| 551 | − headers: { "x-tenant": "acme" }, | |
| 552 | − }), | |
| 553 | − }); | |
| 554 | − | |
| 555 | − await legacyPersistence.db.execute(sql` | |
| 556 | − INSERT INTO "sources" ( | |
| 557 | − "workspace_id", | |
| 558 | − "source_id", | |
| 559 | − "recipe_id", | |
| 560 | − "recipe_revision_id", | |
| 561 | − "name", | |
| 562 | − "kind", | |
| 563 | − "endpoint", | |
| 564 | − "status", | |
| 565 | − "enabled", | |
| 566 | − "namespace", | |
| 567 | − "binding_config_json", | |
| 568 | − "transport", | |
| 569 | − "query_params_json", | |
| 570 | − "headers_json", | |
| 571 | − "spec_url", | |
| 572 | − "default_headers_json", | |
| 573 | − "source_hash", | |
| 574 | − "source_document_text", | |
| 575 | − "last_error", | |
| 576 | − "created_at", | |
| 577 | − "updated_at" | |
| 578 | − ) VALUES | |
| 579 | − ( | |
| 580 | − ${workspaceId}, | |
| 581 | − ${SourceIdSchema.make("src_legacy_openapi")}, | |
| 582 | − ${openapi.recipeId}, | |
| 583 | − ${openapi.recipeRevisionId}, | |
| 584 | − 'Legacy OpenAPI', | |
| 585 | − 'openapi', | |
| 586 | − 'https://api.example.com', | |
| 587 | − 'connected', | |
| 588 | − true, | |
| 589 | − 'legacy.openapi', | |
| 590 | − NULL, | |
| 591 | − NULL, | |
| 592 | − NULL, | |
| 593 | − NULL, | |
| 594 | − 'https://api.example.com/openapi.json', | |
| 595 | − '{"accept":"application/json"}', | |
| 596 | − NULL, | |
| 597 | − NULL, | |
| 598 | − NULL, | |
| 599 | − ${now}, | |
| 600 | − ${now} | |
| 601 | − ), | |
| 602 | − ( | |
| 603 | − ${workspaceId}, | |
| 604 | − ${SourceIdSchema.make("src_legacy_graphql")}, | |
| 605 | − ${graphql.recipeId}, | |
| 606 | − ${graphql.recipeRevisionId}, | |
| 607 | − 'Legacy GraphQL', | |
| 608 | − 'graphql', | |
| 609 | − 'https://api.example.com/graphql', | |
| 610 | − 'connected', | |
| 611 | − true, | |
| 612 | − 'legacy.graphql', | |
| 613 | − NULL, | |
| 614 | − NULL, | |
| 615 | − NULL, | |
| 616 | − NULL, | |
| 617 | − NULL, | |
| 618 | − '{"accept":"application/json"}', | |
| 619 | − NULL, | |
| 620 | − NULL, | |
| 621 | − NULL, | |
| 622 | − ${now}, | |
| 623 | − ${now} | |
| 624 | − ), | |
| 625 | − ( | |
| 626 | − ${workspaceId}, | |
| 627 | − ${SourceIdSchema.make("src_legacy_mcp")}, | |
| 628 | − ${mcp.recipeId}, | |
| 629 | − ${mcp.recipeRevisionId}, | |
| 630 | − 'Legacy MCP', | |
| 631 | − 'mcp', | |
| 632 | − 'https://api.example.com/mcp', | |
| 633 | − 'connected', | |
| 634 | − true, | |
| 635 | − 'legacy.mcp', | |
| 636 | − NULL, | |
| 637 | − 'streamable-http', | |
| 638 | − '{"tenant":"acme"}', | |
| 639 | − '{"x-tenant":"acme"}', | |
| 640 | − NULL, | |
| 641 | − NULL, | |
| 642 | − NULL, | |
| 643 | − NULL, | |
| 644 | − NULL, | |
| 645 | − ${now}, | |
| 646 | − ${now} | |
| 647 | − ); | |
| 648 | − `); | |
| 649 | − | |
| 650 | − await legacyPersistence.close(); | |
| 651 | − legacyPersistence = null; | |
| 652 | − | |
| 653 | − upgradedPersistence = await Effect.runPromise( | |
| 654 | − createSqlControlPlanePersistence({ | |
| 655 | − localDataDir, | |
| 656 | − }), | |
| 657 | − ); | |
| 658 | − | |
| 659 | − const openApiSource = await Effect.runPromise( | |
| 660 | − upgradedPersistence.rows.sources.getByWorkspaceAndId( | |
| 661 | − workspaceId, | |
| 662 | − SourceIdSchema.make("src_legacy_openapi"), | |
| 663 | − ), | |
| 664 | − ); | |
| 665 | − expect( | |
| 666 | − JSON.parse(Option.getOrThrow(openApiSource).bindingConfigJson), | |
| 667 | − ).toEqual({ | |
| 668 | − adapterKey: "openapi", | |
| 669 | − version: 1, | |
| 670 | − payload: { | |
| 671 | − specUrl: "https://api.example.com/openapi.json", | |
| 672 | − defaultHeaders: { | |
| 673 | − accept: "application/json", | |
| 674 | − }, | |
| 675 | − }, | |
| 676 | − }); | |
| 677 | − | |
| 678 | − const graphqlSource = await Effect.runPromise( | |
| 679 | − upgradedPersistence.rows.sources.getByWorkspaceAndId( | |
| 680 | − workspaceId, | |
| 681 | − SourceIdSchema.make("src_legacy_graphql"), | |
| 682 | − ), | |
| 683 | − ); | |
| 684 | − expect( | |
| 685 | − JSON.parse(Option.getOrThrow(graphqlSource).bindingConfigJson), | |
| 686 | − ).toEqual({ | |
| 687 | − adapterKey: "graphql", | |
| 688 | − version: 1, | |
| 689 | − payload: { | |
| 690 | − defaultHeaders: { | |
| 691 | − accept: "application/json", | |
| 692 | − }, | |
| 693 | − }, | |
| 694 | − }); | |
| 695 | − | |
| 696 | − const graphqlRecipe = await Effect.runPromise( | |
| 697 | − upgradedPersistence.rows.sourceRecipes.getById(graphql.recipeId), | |
| 698 | − ); | |
| 699 | − expect(Option.getOrNull(graphqlRecipe)?.adapterKey).toBe("graphql"); | |
| 700 | − | |
| 701 | − const mcpSource = await Effect.runPromise( | |
| 702 | − upgradedPersistence.rows.sources.getByWorkspaceAndId( | |
| 703 | − workspaceId, | |
| 704 | − SourceIdSchema.make("src_legacy_mcp"), | |
| 705 | − ), | |
| 706 | − ); | |
| 707 | − expect( | |
| 708 | − JSON.parse(Option.getOrThrow(mcpSource).bindingConfigJson), | |
| 709 | − ).toEqual({ | |
| 710 | − adapterKey: "mcp", | |
| 711 | − version: 1, | |
| 712 | − payload: { | |
| 713 | − transport: "streamable-http", | |
| 714 | − queryParams: { | |
| 715 | − tenant: "acme", | |
| 716 | − }, | |
| 717 | − headers: { | |
| 718 | − "x-tenant": "acme", | |
| 719 | − }, | |
| 720 | − }, | |
| 721 | − }); | |
| 722 | − | |
| 723 | − const mcpRecipe = await Effect.runPromise( | |
| 724 | − upgradedPersistence.rows.sourceRecipes.getById(mcp.recipeId), | |
| 725 | − ); | |
| 726 | − expect(Option.getOrNull(mcpRecipe)?.adapterKey).toBe("mcp"); | |
| 727 | − } finally { | |
| 728 | − await legacyPersistence?.close().catch(() => undefined); | |
| 729 | − await upgradedPersistence?.close().catch(() => undefined); | |
| 730 | − rmSync(localDataDir, { recursive: true, force: true }); | |
| 731 | − rmSync(legacyMigrationsDir, { recursive: true, force: true }); | |
| 732 | − } | |
| 733 | − }); | |
| 734 | − | |
| 735 | − it("upgrades auth leases into template-based placement storage", async () => { | |
| 736 | − const localDataDir = mkdtempSync(path.join(tmpdir(), "executor-cp-db-")); | |
| 737 | − const legacyMigrationsDir = copyMigrationsBefore( | |
| 738 | − "20260312143000_auth_lease_template_placements", | |
| 739 | − ); | |
| 740 | − const workspaceId = WorkspaceIdSchema.make("ws_legacy_auth_lease"); | |
| 741 | − const accountId = AccountIdSchema.make("acc_legacy_auth_lease"); | |
| 742 | − const sourceId = SourceIdSchema.make("src_legacy_auth_lease"); | |
| 743 | − const recipeId = SourceRecipeIdSchema.make("src_recipe_legacy_auth_lease"); | |
| 744 | − const recipeRevisionId = SourceRecipeRevisionIdSchema.make( | |
| 745 | − "src_recipe_rev_legacy_auth_lease", | |
| 746 | − ); | |
| 747 | − const authArtifactId = "auth_art_legacy_auth_lease"; | |
| 748 | − const now = Date.now(); | |
| 749 | − let legacyPersistence: SqlControlPlanePersistence | null = null; | |
| 750 | − let upgradedPersistence: SqlControlPlanePersistence | null = null; | |
| 751 | − | |
| 752 | − try { | |
| 753 | − legacyPersistence = await Effect.runPromise( | |
| 754 | − createSqlControlPlanePersistence({ | |
| 755 | − localDataDir, | |
| 756 | − migrationsFolder: legacyMigrationsDir, | |
| 757 | − runCodeMigrations: false, | |
| 758 | − }), | |
| 759 | − ); | |
| 760 | − | |
| 761 | − await Effect.runPromise(legacyPersistence.rows.sourceRecipes.upsert({ | |
| 762 | − id: recipeId, | |
| 763 | − kind: "http_api", | |
| 764 | − adapterKey: "openapi", | |
| 765 | − providerKey: "generic_http", | |
| 766 | − name: "Legacy OpenAPI", | |
| 767 | − summary: null, | |
| 768 | − visibility: "workspace", | |
| 769 | − latestRevisionId: recipeRevisionId, | |
| 770 | − createdAt: now, | |
| 771 | − updatedAt: now, | |
| 772 | − })); | |
| 773 | − await Effect.runPromise(legacyPersistence.rows.sourceRecipeRevisions.upsert(baseRevisionRecord({ | |
| 774 | − id: recipeRevisionId, | |
| 775 | − recipeId, | |
| 776 | − revisionNumber: 1, | |
| 777 | − sourceConfigJson: JSON.stringify({ | |
| 778 | − kind: "openapi", | |
| 779 | − endpoint: "https://api.example.com", | |
| 780 | − specUrl: "https://api.example.com/openapi.json", | |
| 781 | − }), | |
| 782 | − createdAt: now, | |
| 783 | − updatedAt: now, | |
| 784 | − }))); | |
| 785 | − await legacyPersistence.db.execute(sql` | |
| 786 | − INSERT INTO "sources" ( | |
| 787 | − "source_id", | |
| 788 | − "workspace_id", | |
| 789 | − "recipe_id", | |
| 790 | − "recipe_revision_id", | |
| 791 | − "name", | |
| 792 | − "kind", | |
| 793 | − "endpoint", | |
| 794 | − "status", | |
| 795 | − "enabled", | |
| 796 | − "namespace", | |
| 797 | − "import_auth_policy", | |
| 798 | − "binding_config_json", | |
| 799 | − "source_hash", | |
| 800 | − "last_error", | |
| 801 | − "created_at", | |
| 802 | − "updated_at" | |
| 803 | − ) VALUES ( | |
| 804 | − ${sourceId}, | |
| 805 | − ${workspaceId}, | |
| 806 | − ${recipeId}, | |
| 807 | − ${recipeRevisionId}, | |
| 808 | − ${"Legacy OpenAPI"}, | |
| 809 | − ${"openapi"}, | |
| 810 | − ${"https://api.example.com"}, | |
| 811 | − ${"connected"}, | |
| 812 | − ${true}, | |
| 813 | − ${"legacy.openapi"}, | |
| 814 | − ${"reuse_runtime"}, | |
| 815 | − ${openApiBindingConfigJson("https://api.example.com/openapi.json")}, | |
| 816 | − NULL, | |
| 817 | − NULL, | |
| 818 | − ${now}, | |
| 819 | − ${now} | |
| 820 | − ) | |
| 821 | − `); | |
| 822 | − await Effect.runPromise(legacyPersistence.rows.authArtifacts.upsert({ | |
| 823 | − id: authArtifactId as any, | |
| 824 | − workspaceId, | |
| 825 | − sourceId, | |
| 826 | − actorAccountId: accountId, | |
| 827 | − slot: "runtime", | |
| 828 | − artifactKind: "static_bearer", | |
| 829 | − configJson: JSON.stringify({ | |
| 830 | − headerName: "Authorization", | |
| 831 | − prefix: "Bearer ", | |
| 832 | − token: { | |
| 833 | − providerId: "postgres", | |
| 834 | − handle: "sec_legacy_auth_lease_token", | |
| 835 | − }, | |
| 836 | − }), | |
| 837 | − grantSetJson: null, | |
| 838 | − createdAt: now, | |
| 839 | − updatedAt: now, | |
| 840 | − })); | |
| 841 | − | |
| 842 | − await legacyPersistence.db.execute(sql` | |
| 843 | − INSERT INTO "workspace_source_auth_leases" ( | |
| 844 | − "id", | |
| 845 | − "auth_artifact_id", | |
| 846 | − "workspace_id", | |
| 847 | − "source_id", | |
| 848 | − "actor_account_id", | |
| 849 | − "slot", | |
| 850 | − "placements_json", | |
| 851 | − "expires_at", | |
| 852 | − "refresh_after", | |
| 853 | − "created_at", | |
| 854 | − "updated_at" | |
| 855 | − ) VALUES ( | |
| 856 | − 'auth_lease_legacy_auth_lease', | |
| 857 | − ${authArtifactId}, | |
| 858 | − ${workspaceId}, | |
| 859 | − ${sourceId}, | |
| 860 | − ${accountId}, | |
| 861 | − 'runtime', | |
| 862 | − '[{"location":"header","name":"Authorization","value":"Bearer abc"},{"location":"query","name":"api_key","value":"q123"},{"location":"cookie","name":"sid","value":"cookie123"},{"location":"body","path":"auth.token","value":"body123"}]', | |
| 863 | − NULL, | |
| 864 | − NULL, | |
| 865 | − ${now}, | |
| 866 | − ${now} | |
| 867 | − ); | |
| 868 | − `); | |
| 869 | − | |
| 870 | − await legacyPersistence.close(); | |
| 871 | − legacyPersistence = null; | |
| 872 | − | |
| 873 | − upgradedPersistence = await Effect.runPromise( | |
| 874 | − createSqlControlPlanePersistence({ | |
| 875 | − localDataDir, | |
| 876 | − }), | |
| 877 | − ); | |
| 878 | − | |
| 879 | − const leaseOption = await Effect.runPromise( | |
| 880 | − upgradedPersistence.rows.authLeases.getByAuthArtifactId(authArtifactId as any), | |
| 881 | − ); | |
| 882 | − expect(Option.isSome(leaseOption)).toBe(true); | |
| 883 | − const lease = Option.getOrNull(leaseOption); | |
| 884 | − expect(lease).not.toBeNull(); | |
| 885 | − expect(lease?.placementsTemplateJson).not.toBeNull(); | |
| 886 | − expect(decodeAuthLeasePlacementTemplates(lease!)).toEqual([ | |
| 887 | − { | |
| 888 | − location: "header", | |
| 889 | − name: "Authorization", | |
| 890 | − parts: [{ kind: "literal", value: "Bearer abc" }], | |
| 891 | − }, | |
| 892 | − { | |
| 893 | − location: "query", | |
| 894 | − name: "api_key", | |
| 895 | − parts: [{ kind: "literal", value: "q123" }], | |
| 896 | − }, | |
| 897 | − { | |
| 898 | − location: "cookie", | |
| 899 | − name: "sid", | |
| 900 | − parts: [{ kind: "literal", value: "cookie123" }], | |
| 901 | − }, | |
| 902 | − { | |
| 903 | − location: "body", | |
| 904 | − path: "auth.token", | |
| 905 | − parts: [{ kind: "literal", value: "body123" }], | |
| 906 | − }, | |
| 907 | − ]); | |
| 908 | − } finally { | |
| 909 | − await legacyPersistence?.close().catch(() => undefined); | |
| 910 | − await upgradedPersistence?.close().catch(() => undefined); | |
| 911 | − rmSync(localDataDir, { recursive: true, force: true }); | |
| 912 | − rmSync(legacyMigrationsDir, { recursive: true, force: true }); | |
| 913 | − } | |
| 914 | − }); | |
| 915 | −}); | |
| @@ -1,71 +0,0 @@ | ||
| 1 | −import { type Account, AccountSchema } from "#schema"; | |
| 2 | −import * as Option from "effect/Option"; | |
| 3 | −import { Schema } from "effect"; | |
| 4 | −import { and, eq } from "drizzle-orm"; | |
| 5 | − | |
| 6 | −import type { DrizzleClient } from "../client"; | |
| 7 | −import type { DrizzleTables } from "../schema"; | |
| 8 | −import { firstOption } from "./shared"; | |
| 9 | − | |
| 10 | −const decodeAccount = Schema.decodeUnknownSync(AccountSchema); | |
| 11 | − | |
| 12 | −export const createAccountsRepo = ( | |
| 13 | − client: DrizzleClient, | |
| 14 | − tables: DrizzleTables, | |
| 15 | −) => ({ | |
| 16 | − getById: (accountId: Account["id"]) => | |
| 17 | − client.use("rows.accounts.get_by_id", async (db) => { | |
| 18 | − const rows = await db | |
| 19 | − .select() | |
| 20 | − .from(tables.accountsTable) | |
| 21 | − .where(eq(tables.accountsTable.id, accountId)) | |
| 22 | − .limit(1); | |
| 23 | − | |
| 24 | − const row = firstOption(rows); | |
| 25 | − return Option.isSome(row) | |
| 26 | − ? Option.some(decodeAccount(row.value)) | |
| 27 | − : Option.none<Account>(); | |
| 28 | − }), | |
| 29 | − | |
| 30 | − getByProviderAndSubject: ( | |
| 31 | − provider: Account["provider"], | |
| 32 | − subject: Account["subject"], | |
| 33 | − ) => | |
| 34 | − client.use("rows.accounts.get_by_provider_and_subject", async (db) => { | |
| 35 | − const rows = await db | |
| 36 | − .select() | |
| 37 | − .from(tables.accountsTable) | |
| 38 | − .where( | |
| 39 | − and( | |
| 40 | − eq(tables.accountsTable.provider, provider), | |
| 41 | − eq(tables.accountsTable.subject, subject), | |
| 42 | − ), | |
| 43 | − ) | |
| 44 | − .limit(1); | |
| 45 | − | |
| 46 | − const row = firstOption(rows); | |
| 47 | − return Option.isSome(row) | |
| 48 | − ? Option.some(decodeAccount(row.value)) | |
| 49 | − : Option.none<Account>(); | |
| 50 | − }), | |
| 51 | − | |
| 52 | − insert: (account: Account) => | |
| 53 | − client.use("rows.accounts.insert", async (db) => { | |
| 54 | − await db.insert(tables.accountsTable).values(account); | |
| 55 | − }), | |
| 56 | − | |
| 57 | − upsert: (account: Account) => | |
| 58 | − client.use("rows.accounts.upsert", async (db) => { | |
| 59 | − await db | |
| 60 | − .insert(tables.accountsTable) | |
| 61 | − .values(account) | |
| 62 | − .onConflictDoUpdate({ | |
| 63 | − target: [tables.accountsTable.provider, tables.accountsTable.subject], | |
| 64 | − set: { | |
| 65 | − email: account.email, | |
| 66 | − displayName: account.displayName, | |
| 67 | − updatedAt: account.updatedAt, | |
| 68 | − }, | |
| 69 | − }); | |
| 70 | − }), | |
| 71 | −}); | |
| @@ -1,226 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − AuthArtifactSchema, | |
| 3 | − type AuthArtifact, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, asc, eq, inArray, isNull, or } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeAuthArtifact = Schema.decodeUnknownSync(AuthArtifactSchema); | |
| 14 | − | |
| 15 | −const authArtifactUpdateSet = (artifact: AuthArtifact) => { | |
| 16 | − const { | |
| 17 | − id: _id, | |
| 18 | − workspaceId: _workspaceId, | |
| 19 | − sourceId: _sourceId, | |
| 20 | − actorAccountId: _actorAccountId, | |
| 21 | − createdAt: _createdAt, | |
| 22 | − ...patch | |
| 23 | − } = artifact; | |
| 24 | − return patch; | |
| 25 | −}; | |
| 26 | − | |
| 27 | −export const createAuthArtifactsRepo = ( | |
| 28 | − client: DrizzleClient, | |
| 29 | − tables: DrizzleTables, | |
| 30 | −) => ({ | |
| 31 | − listByWorkspaceId: (workspaceId: AuthArtifact["workspaceId"]) => | |
| 32 | − client.use("rows.auth_artifacts.list_by_workspace", async (db) => { | |
| 33 | − const rows = await db | |
| 34 | − .select() | |
| 35 | − .from(tables.authArtifactsTable) | |
| 36 | − .where(eq(tables.authArtifactsTable.workspaceId, workspaceId)) | |
| 37 | − .orderBy( | |
| 38 | − asc(tables.authArtifactsTable.updatedAt), | |
| 39 | − asc(tables.authArtifactsTable.id), | |
| 40 | − ); | |
| 41 | − | |
| 42 | − return rows.map((row) => decodeAuthArtifact(row)); | |
| 43 | − }), | |
| 44 | − | |
| 45 | − listByWorkspaceAndSourceId: (input: { | |
| 46 | − workspaceId: AuthArtifact["workspaceId"]; | |
| 47 | − sourceId: AuthArtifact["sourceId"]; | |
| 48 | − }) => | |
| 49 | − client.use("rows.auth_artifacts.list_by_workspace_source", async (db) => { | |
| 50 | − const rows = await db | |
| 51 | − .select() | |
| 52 | − .from(tables.authArtifactsTable) | |
| 53 | − .where( | |
| 54 | − and( | |
| 55 | − eq(tables.authArtifactsTable.workspaceId, input.workspaceId), | |
| 56 | − eq(tables.authArtifactsTable.sourceId, input.sourceId), | |
| 57 | − ), | |
| 58 | − ) | |
| 59 | − .orderBy( | |
| 60 | − asc(tables.authArtifactsTable.updatedAt), | |
| 61 | − asc(tables.authArtifactsTable.id), | |
| 62 | − ); | |
| 63 | − | |
| 64 | − return rows.map((row) => decodeAuthArtifact(row)); | |
| 65 | − }), | |
| 66 | − | |
| 67 | − listByWorkspaceSourceAndActor: (input: { | |
| 68 | − workspaceId: AuthArtifact["workspaceId"]; | |
| 69 | − sourceId: AuthArtifact["sourceId"]; | |
| 70 | − actorAccountId: AuthArtifact["actorAccountId"]; | |
| 71 | − }) => | |
| 72 | − client.use("rows.auth_artifacts.list_by_workspace_source_actor", async (db) => { | |
| 73 | − const rows = await db | |
| 74 | − .select() | |
| 75 | − .from(tables.authArtifactsTable) | |
| 76 | − .where( | |
| 77 | − and( | |
| 78 | − eq(tables.authArtifactsTable.workspaceId, input.workspaceId), | |
| 79 | − eq(tables.authArtifactsTable.sourceId, input.sourceId), | |
| 80 | − or( | |
| 81 | − input.actorAccountId === null | |
| 82 | − ? isNull(tables.authArtifactsTable.actorAccountId) | |
| 83 | − : eq(tables.authArtifactsTable.actorAccountId, input.actorAccountId), | |
| 84 | − input.actorAccountId === null | |
| 85 | − ? undefined | |
| 86 | − : isNull(tables.authArtifactsTable.actorAccountId), | |
| 87 | − ), | |
| 88 | − ), | |
| 89 | − ) | |
| 90 | − .orderBy( | |
| 91 | − asc(tables.authArtifactsTable.slot), | |
| 92 | − asc(tables.authArtifactsTable.actorAccountId), | |
| 93 | − asc(tables.authArtifactsTable.updatedAt), | |
| 94 | − asc(tables.authArtifactsTable.id), | |
| 95 | − ); | |
| 96 | − | |
| 97 | − return rows.map((row) => decodeAuthArtifact(row)); | |
| 98 | − }), | |
| 99 | − | |
| 100 | − getByWorkspaceSourceAndActor: (input: { | |
| 101 | − workspaceId: AuthArtifact["workspaceId"]; | |
| 102 | − sourceId: AuthArtifact["sourceId"]; | |
| 103 | − actorAccountId: AuthArtifact["actorAccountId"]; | |
| 104 | − slot: AuthArtifact["slot"]; | |
| 105 | − }) => | |
| 106 | − client.use("rows.auth_artifacts.get_by_workspace_source_actor", async (db) => { | |
| 107 | − const rows = await db | |
| 108 | − .select() | |
| 109 | − .from(tables.authArtifactsTable) | |
| 110 | − .where( | |
| 111 | − and( | |
| 112 | − eq(tables.authArtifactsTable.workspaceId, input.workspaceId), | |
| 113 | − eq(tables.authArtifactsTable.sourceId, input.sourceId), | |
| 114 | − eq(tables.authArtifactsTable.slot, input.slot), | |
| 115 | − input.actorAccountId === null | |
| 116 | − ? isNull(tables.authArtifactsTable.actorAccountId) | |
| 117 | − : eq(tables.authArtifactsTable.actorAccountId, input.actorAccountId), | |
| 118 | − ), | |
| 119 | − ) | |
| 120 | − .limit(1); | |
| 121 | − | |
| 122 | − const row = firstOption(rows); | |
| 123 | − return Option.isSome(row) | |
| 124 | − ? Option.some(decodeAuthArtifact(row.value)) | |
| 125 | − : Option.none<AuthArtifact>(); | |
| 126 | − }), | |
| 127 | − | |
| 128 | − upsert: (artifact: AuthArtifact) => | |
| 129 | − client.use("rows.auth_artifacts.upsert", async (db) => { | |
| 130 | − if (artifact.actorAccountId === null) { | |
| 131 | − const existingRows = await db | |
| 132 | − .select({ | |
| 133 | − id: tables.authArtifactsTable.id, | |
| 134 | − }) | |
| 135 | − .from(tables.authArtifactsTable) | |
| 136 | − .where( | |
| 137 | − and( | |
| 138 | − eq(tables.authArtifactsTable.workspaceId, artifact.workspaceId), | |
| 139 | − eq(tables.authArtifactsTable.sourceId, artifact.sourceId), | |
| 140 | − eq(tables.authArtifactsTable.slot, artifact.slot), | |
| 141 | − isNull(tables.authArtifactsTable.actorAccountId), | |
| 142 | − ), | |
| 143 | − ) | |
| 144 | − .orderBy( | |
| 145 | − asc(tables.authArtifactsTable.updatedAt), | |
| 146 | − asc(tables.authArtifactsTable.id), | |
| 147 | − ); | |
| 148 | − | |
| 149 | − const existing = firstOption(existingRows); | |
| 150 | − if (Option.isSome(existing)) { | |
| 151 | − await db | |
| 152 | − .update(tables.authArtifactsTable) | |
| 153 | − .set(authArtifactUpdateSet(artifact)) | |
| 154 | − .where(eq(tables.authArtifactsTable.id, existing.value.id)); | |
| 155 | − | |
| 156 | − const duplicateIds = existingRows.slice(1).map((row) => row.id); | |
| 157 | − if (duplicateIds.length > 0) { | |
| 158 | − await db | |
| 159 | − .delete(tables.authArtifactsTable) | |
| 160 | − .where(inArray(tables.authArtifactsTable.id, duplicateIds)); | |
| 161 | − } | |
| 162 | − | |
| 163 | − return; | |
| 164 | − } | |
| 165 | − } | |
| 166 | − | |
| 167 | − await db | |
| 168 | − .insert(tables.authArtifactsTable) | |
| 169 | − .values(artifact) | |
| 170 | − .onConflictDoUpdate({ | |
| 171 | − target: [ | |
| 172 | − tables.authArtifactsTable.workspaceId, | |
| 173 | − tables.authArtifactsTable.sourceId, | |
| 174 | − tables.authArtifactsTable.actorAccountId, | |
| 175 | − tables.authArtifactsTable.slot, | |
| 176 | − ], | |
| 177 | − set: { | |
| 178 | − ...authArtifactUpdateSet(artifact), | |
| 179 | − }, | |
| 180 | − }); | |
| 181 | − }), | |
| 182 | − | |
| 183 | − removeByWorkspaceSourceAndActor: (input: { | |
| 184 | − workspaceId: AuthArtifact["workspaceId"]; | |
| 185 | − sourceId: AuthArtifact["sourceId"]; | |
| 186 | − actorAccountId: AuthArtifact["actorAccountId"]; | |
| 187 | − slot?: AuthArtifact["slot"]; | |
| 188 | − }) => | |
| 189 | − client.use("rows.auth_artifacts.remove_by_workspace_source_actor", async (db) => { | |
| 190 | − const deleted = await db | |
| 191 | − .delete(tables.authArtifactsTable) | |
| 192 | − .where( | |
| 193 | − and( | |
| 194 | − eq(tables.authArtifactsTable.workspaceId, input.workspaceId), | |
| 195 | − eq(tables.authArtifactsTable.sourceId, input.sourceId), | |
| 196 | − input.slot === undefined | |
| 197 | − ? undefined | |
| 198 | − : eq(tables.authArtifactsTable.slot, input.slot), | |
| 199 | − input.actorAccountId === null | |
| 200 | − ? isNull(tables.authArtifactsTable.actorAccountId) | |
| 201 | − : eq(tables.authArtifactsTable.actorAccountId, input.actorAccountId), | |
| 202 | − ), | |
| 203 | − ) | |
| 204 | − .returning(); | |
| 205 | − | |
| 206 | − return deleted.length > 0; | |
| 207 | − }), | |
| 208 | − | |
| 209 | − removeByWorkspaceAndSourceId: (input: { | |
| 210 | − workspaceId: AuthArtifact["workspaceId"]; | |
| 211 | − sourceId: AuthArtifact["sourceId"]; | |
| 212 | − }) => | |
| 213 | − client.use("rows.auth_artifacts.remove_by_workspace_source", async (db) => { | |
| 214 | − const deleted = await db | |
| 215 | − .delete(tables.authArtifactsTable) | |
| 216 | − .where( | |
| 217 | − and( | |
| 218 | − eq(tables.authArtifactsTable.workspaceId, input.workspaceId), | |
| 219 | − eq(tables.authArtifactsTable.sourceId, input.sourceId), | |
| 220 | − ), | |
| 221 | − ) | |
| 222 | − .returning(); | |
| 223 | − | |
| 224 | − return deleted.length; | |
| 225 | − }), | |
| 226 | −}); | |
| @@ -1,145 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − AuthLeaseSchema, | |
| 3 | − type AuthLease, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, asc, eq, isNull } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeAuthLease = Schema.decodeUnknownSync(AuthLeaseSchema); | |
| 14 | − | |
| 15 | −export const createAuthLeasesRepo = ( | |
| 16 | − client: DrizzleClient, | |
| 17 | − tables: DrizzleTables, | |
| 18 | −) => ({ | |
| 19 | − getByAuthArtifactId: (authArtifactId: AuthLease["authArtifactId"]) => | |
| 20 | − client.use("rows.auth_leases.get_by_auth_artifact_id", async (db) => { | |
| 21 | − const rows = await db | |
| 22 | − .select() | |
| 23 | − .from(tables.authLeasesTable) | |
| 24 | − .where(eq(tables.authLeasesTable.authArtifactId, authArtifactId)) | |
| 25 | − .limit(1); | |
| 26 | − | |
| 27 | − const row = firstOption(rows); | |
| 28 | − return Option.isSome(row) | |
| 29 | − ? Option.some(decodeAuthLease(row.value)) | |
| 30 | − : Option.none<AuthLease>(); | |
| 31 | − }), | |
| 32 | − | |
| 33 | − listByWorkspaceId: (workspaceId: AuthLease["workspaceId"]) => | |
| 34 | − client.use("rows.auth_leases.list_by_workspace", async (db) => { | |
| 35 | − const rows = await db | |
| 36 | − .select() | |
| 37 | − .from(tables.authLeasesTable) | |
| 38 | − .where(eq(tables.authLeasesTable.workspaceId, workspaceId)) | |
| 39 | − .orderBy( | |
| 40 | − asc(tables.authLeasesTable.updatedAt), | |
| 41 | − asc(tables.authLeasesTable.id), | |
| 42 | − ); | |
| 43 | − | |
| 44 | − return rows.map((row) => decodeAuthLease(row)); | |
| 45 | − }), | |
| 46 | − | |
| 47 | − getByWorkspaceSourceAndActor: (input: { | |
| 48 | − workspaceId: AuthLease["workspaceId"]; | |
| 49 | − sourceId: AuthLease["sourceId"]; | |
| 50 | − actorAccountId: AuthLease["actorAccountId"]; | |
| 51 | − slot: AuthLease["slot"]; | |
| 52 | − }) => | |
| 53 | − client.use("rows.auth_leases.get_by_workspace_source_actor", async (db) => { | |
| 54 | − const rows = await db | |
| 55 | − .select() | |
| 56 | − .from(tables.authLeasesTable) | |
| 57 | − .where( | |
| 58 | − and( | |
| 59 | − eq(tables.authLeasesTable.workspaceId, input.workspaceId), | |
| 60 | − eq(tables.authLeasesTable.sourceId, input.sourceId), | |
| 61 | − eq(tables.authLeasesTable.slot, input.slot), | |
| 62 | − input.actorAccountId === null | |
| 63 | − ? isNull(tables.authLeasesTable.actorAccountId) | |
| 64 | − : eq(tables.authLeasesTable.actorAccountId, input.actorAccountId), | |
| 65 | − ), | |
| 66 | − ) | |
| 67 | − .orderBy( | |
| 68 | − asc(tables.authLeasesTable.updatedAt), | |
| 69 | − asc(tables.authLeasesTable.id), | |
| 70 | − ) | |
| 71 | − .limit(1); | |
| 72 | − | |
| 73 | − const row = firstOption(rows); | |
| 74 | − return Option.isSome(row) | |
| 75 | − ? Option.some(decodeAuthLease(row.value)) | |
| 76 | − : Option.none<AuthLease>(); | |
| 77 | − }), | |
| 78 | − | |
| 79 | − upsert: (lease: AuthLease) => | |
| 80 | − client.use("rows.auth_leases.upsert", async (db) => { | |
| 81 | − await db | |
| 82 | − .insert(tables.authLeasesTable) | |
| 83 | − .values(lease) | |
| 84 | − .onConflictDoUpdate({ | |
| 85 | − target: [tables.authLeasesTable.authArtifactId], | |
| 86 | − set: { | |
| 87 | − ...withoutCreatedAt(lease), | |
| 88 | − }, | |
| 89 | − }); | |
| 90 | − }), | |
| 91 | − | |
| 92 | − removeByAuthArtifactId: (authArtifactId: AuthLease["authArtifactId"]) => | |
| 93 | − client.use("rows.auth_leases.remove_by_auth_artifact_id", async (db) => { | |
| 94 | − const deleted = await db | |
| 95 | − .delete(tables.authLeasesTable) | |
| 96 | − .where(eq(tables.authLeasesTable.authArtifactId, authArtifactId)) | |
| 97 | − .returning(); | |
| 98 | − | |
| 99 | − return deleted.length > 0; | |
| 100 | − }), | |
| 101 | − | |
| 102 | − removeByWorkspaceSourceAndActor: (input: { | |
| 103 | − workspaceId: AuthLease["workspaceId"]; | |
| 104 | − sourceId: AuthLease["sourceId"]; | |
| 105 | − actorAccountId: AuthLease["actorAccountId"]; | |
| 106 | − slot?: AuthLease["slot"]; | |
| 107 | − }) => | |
| 108 | − client.use("rows.auth_leases.remove_by_workspace_source_actor", async (db) => { | |
| 109 | − const deleted = await db | |
| 110 | − .delete(tables.authLeasesTable) | |
| 111 | − .where( | |
| 112 | − and( | |
| 113 | − eq(tables.authLeasesTable.workspaceId, input.workspaceId), | |
| 114 | − eq(tables.authLeasesTable.sourceId, input.sourceId), | |
| 115 | − input.slot === undefined | |
| 116 | − ? undefined | |
| 117 | − : eq(tables.authLeasesTable.slot, input.slot), | |
| 118 | − input.actorAccountId === null | |
| 119 | − ? isNull(tables.authLeasesTable.actorAccountId) | |
| 120 | − : eq(tables.authLeasesTable.actorAccountId, input.actorAccountId), | |
| 121 | − ), | |
| 122 | − ) | |
| 123 | − .returning(); | |
| 124 | − | |
| 125 | − return deleted.length > 0; | |
| 126 | − }), | |
| 127 | − | |
| 128 | − removeByWorkspaceAndSourceId: (input: { | |
| 129 | − workspaceId: AuthLease["workspaceId"]; | |
| 130 | − sourceId: AuthLease["sourceId"]; | |
| 131 | − }) => | |
| 132 | − client.use("rows.auth_leases.remove_by_workspace_source", async (db) => { | |
| 133 | − const deleted = await db | |
| 134 | − .delete(tables.authLeasesTable) | |
| 135 | − .where( | |
| 136 | − and( | |
| 137 | − eq(tables.authLeasesTable.workspaceId, input.workspaceId), | |
| 138 | − eq(tables.authLeasesTable.sourceId, input.sourceId), | |
| 139 | − ), | |
| 140 | − ) | |
| 141 | − .returning(); | |
| 142 | − | |
| 143 | − return deleted.length; | |
| 144 | − }), | |
| 145 | −}); | |
| @@ -1,41 +0,0 @@ | ||
| 1 | −import type { | |
| 2 | − StoredCodeMigrationRecord, | |
| 3 | −} from "#schema"; | |
| 4 | −import { StoredCodeMigrationRecordSchema } from "#schema"; | |
| 5 | −import { Schema } from "effect"; | |
| 6 | −import { asc } from "drizzle-orm"; | |
| 7 | − | |
| 8 | −import type { DrizzleClient } from "../client"; | |
| 9 | −import type { DrizzleTables } from "../schema"; | |
| 10 | − | |
| 11 | −const decodeStoredCodeMigrationRecord = Schema.decodeUnknownSync( | |
| 12 | − StoredCodeMigrationRecordSchema, | |
| 13 | −); | |
| 14 | − | |
| 15 | −export const createCodeMigrationsRepo = ( | |
| 16 | − client: DrizzleClient, | |
| 17 | − tables: DrizzleTables, | |
| 18 | −) => ({ | |
| 19 | − listAll: () => | |
| 20 | − client.use("rows.code_migrations.list_all", async (db) => { | |
| 21 | − const rows = await db | |
| 22 | − .select() | |
| 23 | − .from(tables.codeMigrationsTable) | |
| 24 | − .orderBy(asc(tables.codeMigrationsTable.appliedAt)); | |
| 25 | − | |
| 26 | − return rows.map((row) => decodeStoredCodeMigrationRecord(row)); | |
| 27 | − }), | |
| 28 | − | |
| 29 | − upsert: (record: StoredCodeMigrationRecord) => | |
| 30 | − client.use("rows.code_migrations.upsert", async (db) => { | |
| 31 | − await db | |
| 32 | − .insert(tables.codeMigrationsTable) | |
| 33 | − .values(record) | |
| 34 | − .onConflictDoNothing(); | |
| 35 | − }), | |
| 36 | − | |
| 37 | − clearAll: () => | |
| 38 | − client.use("rows.code_migrations.clear_all", async (db) => { | |
| 39 | − await db.delete(tables.codeMigrationsTable); | |
| 40 | − }), | |
| 41 | −}); | |
| @@ -1,96 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type ExecutionInteraction, | |
| 3 | − ExecutionInteractionSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, desc, eq } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeExecutionInteraction = Schema.decodeUnknownSync( | |
| 14 | − ExecutionInteractionSchema, | |
| 15 | −); | |
| 16 | − | |
| 17 | −export const createExecutionInteractionsRepo = ( | |
| 18 | − client: DrizzleClient, | |
| 19 | − tables: DrizzleTables, | |
| 20 | −) => ({ | |
| 21 | − getById: (interactionId: ExecutionInteraction["id"]) => | |
| 22 | − client.use("rows.execution_interactions.get_by_id", async (db) => { | |
| 23 | − const rows = await db | |
| 24 | − .select() | |
| 25 | − .from(tables.executionInteractionsTable) | |
| 26 | − .where(eq(tables.executionInteractionsTable.id, interactionId)) | |
| 27 | − .limit(1); | |
| 28 | − | |
| 29 | − const row = firstOption(rows); | |
| 30 | − return Option.isSome(row) | |
| 31 | − ? Option.some(decodeExecutionInteraction(row.value)) | |
| 32 | − : Option.none<ExecutionInteraction>(); | |
| 33 | − }), | |
| 34 | − | |
| 35 | − listByExecutionId: (executionId: ExecutionInteraction["executionId"]) => | |
| 36 | − client.use("rows.execution_interactions.list_by_execution_id", async (db) => { | |
| 37 | − const rows = await db | |
| 38 | − .select() | |
| 39 | − .from(tables.executionInteractionsTable) | |
| 40 | − .where(eq(tables.executionInteractionsTable.executionId, executionId)) | |
| 41 | − .orderBy( | |
| 42 | − desc(tables.executionInteractionsTable.updatedAt), | |
| 43 | − desc(tables.executionInteractionsTable.id), | |
| 44 | − ); | |
| 45 | − | |
| 46 | − return rows.map((row) => decodeExecutionInteraction(row)); | |
| 47 | − }), | |
| 48 | − | |
| 49 | − getPendingByExecutionId: (executionId: ExecutionInteraction["executionId"]) => | |
| 50 | − client.use( | |
| 51 | − "rows.execution_interactions.get_pending_by_execution_id", | |
| 52 | − async (db) => { | |
| 53 | − const rows = await db | |
| 54 | − .select() | |
| 55 | − .from(tables.executionInteractionsTable) | |
| 56 | − .where( | |
| 57 | − and( | |
| 58 | − eq(tables.executionInteractionsTable.executionId, executionId), | |
| 59 | − eq(tables.executionInteractionsTable.status, "pending"), | |
| 60 | − ), | |
| 61 | − ) | |
| 62 | − .orderBy( | |
| 63 | − desc(tables.executionInteractionsTable.updatedAt), | |
| 64 | − desc(tables.executionInteractionsTable.id), | |
| 65 | − ) | |
| 66 | − .limit(1); | |
| 67 | − | |
| 68 | − const row = firstOption(rows); | |
| 69 | − return Option.isSome(row) | |
| 70 | − ? Option.some(decodeExecutionInteraction(row.value)) | |
| 71 | − : Option.none<ExecutionInteraction>(); | |
| 72 | − }, | |
| 73 | − ), | |
| 74 | − | |
| 75 | − insert: (interaction: ExecutionInteraction) => | |
| 76 | − client.use("rows.execution_interactions.insert", async (db) => { | |
| 77 | − await db.insert(tables.executionInteractionsTable).values(interaction); | |
| 78 | − }), | |
| 79 | − | |
| 80 | − update: ( | |
| 81 | − interactionId: ExecutionInteraction["id"], | |
| 82 | − patch: Partial<Omit<ExecutionInteraction, "id" | "executionId" | "createdAt">>, | |
| 83 | − ) => | |
| 84 | − client.use("rows.execution_interactions.update", async (db) => { | |
| 85 | − const rows = await db | |
| 86 | − .update(tables.executionInteractionsTable) | |
| 87 | − .set(patch) | |
| 88 | − .where(eq(tables.executionInteractionsTable.id, interactionId)) | |
| 89 | − .returning(); | |
| 90 | − | |
| 91 | − const row = firstOption(rows); | |
| 92 | − return Option.isSome(row) | |
| 93 | − ? Option.some(decodeExecutionInteraction(row.value)) | |
| 94 | − : Option.none<ExecutionInteraction>(); | |
| 95 | − }), | |
| 96 | −}); | |
| @@ -1,89 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type ExecutionStep, | |
| 3 | − ExecutionStepSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, desc, eq } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeExecutionStep = Schema.decodeUnknownSync(ExecutionStepSchema); | |
| 14 | − | |
| 15 | −export const createExecutionStepsRepo = ( | |
| 16 | − client: DrizzleClient, | |
| 17 | − tables: DrizzleTables, | |
| 18 | −) => ({ | |
| 19 | − getByExecutionAndSequence: ( | |
| 20 | − executionId: ExecutionStep["executionId"], | |
| 21 | − sequence: ExecutionStep["sequence"], | |
| 22 | − ) => | |
| 23 | − client.use("rows.execution_steps.get_by_execution_and_sequence", async (db) => { | |
| 24 | − const rows = await db | |
| 25 | − .select() | |
| 26 | − .from(tables.executionStepsTable) | |
| 27 | − .where( | |
| 28 | − and( | |
| 29 | − eq(tables.executionStepsTable.executionId, executionId), | |
| 30 | − eq(tables.executionStepsTable.sequence, sequence), | |
| 31 | − ), | |
| 32 | − ) | |
| 33 | − .limit(1); | |
| 34 | − | |
| 35 | − const row = firstOption(rows); | |
| 36 | − return Option.isSome(row) | |
| 37 | − ? Option.some(decodeExecutionStep(row.value)) | |
| 38 | − : Option.none<ExecutionStep>(); | |
| 39 | − }), | |
| 40 | − | |
| 41 | − listByExecutionId: (executionId: ExecutionStep["executionId"]) => | |
| 42 | − client.use("rows.execution_steps.list_by_execution_id", async (db) => { | |
| 43 | − const rows = await db | |
| 44 | − .select() | |
| 45 | − .from(tables.executionStepsTable) | |
| 46 | − .where(eq(tables.executionStepsTable.executionId, executionId)) | |
| 47 | − .orderBy( | |
| 48 | − tables.executionStepsTable.sequence, | |
| 49 | − desc(tables.executionStepsTable.updatedAt), | |
| 50 | − ); | |
| 51 | − | |
| 52 | − return rows.map((row) => decodeExecutionStep(row)); | |
| 53 | − }), | |
| 54 | − | |
| 55 | − insert: (step: ExecutionStep) => | |
| 56 | − client.use("rows.execution_steps.insert", async (db) => { | |
| 57 | − await db.insert(tables.executionStepsTable).values(step); | |
| 58 | − }), | |
| 59 | − | |
| 60 | − deleteByExecutionId: (executionId: ExecutionStep["executionId"]) => | |
| 61 | − client.use("rows.execution_steps.delete_by_execution_id", async (db) => { | |
| 62 | − await db | |
| 63 | − .delete(tables.executionStepsTable) | |
| 64 | − .where(eq(tables.executionStepsTable.executionId, executionId)); | |
| 65 | − }), | |
| 66 | − | |
| 67 | − updateByExecutionAndSequence: ( | |
| 68 | − executionId: ExecutionStep["executionId"], | |
| 69 | − sequence: ExecutionStep["sequence"], | |
| 70 | − patch: Partial<Omit<ExecutionStep, "id" | "executionId" | "sequence" | "createdAt">>, | |
| 71 | − ) => | |
| 72 | − client.use("rows.execution_steps.update_by_execution_and_sequence", async (db) => { | |
| 73 | − const rows = await db | |
| 74 | − .update(tables.executionStepsTable) | |
| 75 | − .set(patch) | |
| 76 | − .where( | |
| 77 | − and( | |
| 78 | − eq(tables.executionStepsTable.executionId, executionId), | |
| 79 | − eq(tables.executionStepsTable.sequence, sequence), | |
| 80 | − ), | |
| 81 | − ) | |
| 82 | − .returning(); | |
| 83 | − | |
| 84 | − const row = firstOption(rows); | |
| 85 | − return Option.isSome(row) | |
| 86 | − ? Option.some(decodeExecutionStep(row.value)) | |
| 87 | − : Option.none<ExecutionStep>(); | |
| 88 | − }), | |
| 89 | −}); | |
| @@ -1,75 +0,0 @@ | ||
| 1 | −import { type Execution, ExecutionSchema } from "#schema"; | |
| 2 | −import * as Option from "effect/Option"; | |
| 3 | −import { Schema } from "effect"; | |
| 4 | −import { and, eq } from "drizzle-orm"; | |
| 5 | − | |
| 6 | −import type { DrizzleClient } from "../client"; | |
| 7 | −import type { DrizzleTables } from "../schema"; | |
| 8 | −import { firstOption } from "./shared"; | |
| 9 | − | |
| 10 | −const decodeExecution = Schema.decodeUnknownSync(ExecutionSchema); | |
| 11 | − | |
| 12 | −export const createExecutionsRepo = ( | |
| 13 | − client: DrizzleClient, | |
| 14 | − tables: DrizzleTables, | |
| 15 | −) => ({ | |
| 16 | − getById: (executionId: Execution["id"]) => | |
| 17 | − client.use("rows.executions.get_by_id", async (db) => { | |
| 18 | − const rows = await db | |
| 19 | − .select() | |
| 20 | − .from(tables.executionsTable) | |
| 21 | − .where(eq(tables.executionsTable.id, executionId)) | |
| 22 | − .limit(1); | |
| 23 | − | |
| 24 | − const row = firstOption(rows); | |
| 25 | − return Option.isSome(row) | |
| 26 | − ? Option.some(decodeExecution(row.value)) | |
| 27 | − : Option.none<Execution>(); | |
| 28 | − }), | |
| 29 | − | |
| 30 | − getByWorkspaceAndId: ( | |
| 31 | − workspaceId: Execution["workspaceId"], | |
| 32 | − executionId: Execution["id"], | |
| 33 | − ) => | |
| 34 | − client.use("rows.executions.get_by_workspace_and_id", async (db) => { | |
| 35 | − const rows = await db | |
| 36 | − .select() | |
| 37 | − .from(tables.executionsTable) | |
| 38 | − .where( | |
| 39 | − and( | |
| 40 | − eq(tables.executionsTable.workspaceId, workspaceId), | |
| 41 | − eq(tables.executionsTable.id, executionId), | |
| 42 | − ), | |
| 43 | − ) | |
| 44 | − .limit(1); | |
| 45 | − | |
| 46 | − const row = firstOption(rows); | |
| 47 | − return Option.isSome(row) | |
| 48 | − ? Option.some(decodeExecution(row.value)) | |
| 49 | − : Option.none<Execution>(); | |
| 50 | − }), | |
| 51 | − | |
| 52 | − insert: (execution: Execution) => | |
| 53 | − client.use("rows.executions.insert", async (db) => { | |
| 54 | − await db.insert(tables.executionsTable).values(execution); | |
| 55 | − }), | |
| 56 | − | |
| 57 | − update: ( | |
| 58 | − executionId: Execution["id"], | |
| 59 | − patch: Partial< | |
| 60 | − Omit<Execution, "id" | "workspaceId" | "createdByAccountId" | "createdAt"> | |
| 61 | − >, | |
| 62 | − ) => | |
| 63 | − client.use("rows.executions.update", async (db) => { | |
| 64 | − const rows = await db | |
| 65 | − .update(tables.executionsTable) | |
| 66 | − .set(patch) | |
| 67 | − .where(eq(tables.executionsTable.id, executionId)) | |
| 68 | − .returning(); | |
| 69 | − | |
| 70 | − const row = firstOption(rows); | |
| 71 | − return Option.isSome(row) | |
| 72 | − ? Option.some(decodeExecution(row.value)) | |
| 73 | − : Option.none<Execution>(); | |
| 74 | − }), | |
| 75 | −}); | |
| @@ -1,16 +0,0 @@ | ||
| 1 | −export { createAccountsRepo } from "./accounts-repo"; | |
| 2 | −export { createSourcesRepo } from "./sources-repo"; | |
| 3 | −export { createSourceRecipesRepo } from "./source-recipes-repo"; | |
| 4 | −export { createSourceRecipeRevisionsRepo } from "./source-recipe-revisions-repo"; | |
| 5 | −export { createSourceRecipeDocumentsRepo } from "./source-recipe-documents-repo"; | |
| 6 | −export { createSourceRecipeSchemaBundlesRepo } from "./source-recipe-schema-bundles-repo"; | |
| 7 | −export { createSourceRecipeOperationsRepo } from "./source-recipe-operations-repo"; | |
| 8 | −export { createCodeMigrationsRepo } from "./code-migrations-repo"; | |
| 9 | −export { createAuthArtifactsRepo } from "./auth-artifacts-repo"; | |
| 10 | −export { createAuthLeasesRepo } from "./auth-leases-repo"; | |
| 11 | −export { createSourceOauthClientsRepo } from "./source-oauth-clients-repo"; | |
| 12 | −export { createSecretMaterialsRepo } from "./secret-materials-repo"; | |
| 13 | −export { createSourceAuthSessionsRepo } from "./source-auth-sessions-repo"; | |
| 14 | −export { createExecutionsRepo } from "./executions-repo"; | |
| 15 | −export { createExecutionInteractionsRepo } from "./execution-interactions-repo"; | |
| 16 | −export { createExecutionStepsRepo } from "./execution-steps-repo"; | |
| @@ -1,144 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − authArtifactSecretRefs, | |
| 3 | − type SecretMaterial, | |
| 4 | − SecretMaterialSchema, | |
| 5 | −} from "#schema"; | |
| 6 | −import * as Option from "effect/Option"; | |
| 7 | −import { Schema } from "effect"; | |
| 8 | −import { eq, desc, and } from "drizzle-orm"; | |
| 9 | − | |
| 10 | −import type { DrizzleClient } from "../client"; | |
| 11 | −import type { DrizzleTables } from "../schema"; | |
| 12 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 13 | − | |
| 14 | −const decodeSecretMaterial = Schema.decodeUnknownSync(SecretMaterialSchema); | |
| 15 | − | |
| 16 | −export const createSecretMaterialsRepo = ( | |
| 17 | − client: DrizzleClient, | |
| 18 | − tables: DrizzleTables, | |
| 19 | −) => ({ | |
| 20 | − getById: (id: SecretMaterial["id"]) => | |
| 21 | − client.use("rows.secret_materials.get_by_id", async (db) => { | |
| 22 | − const rows = await db | |
| 23 | − .select() | |
| 24 | − .from(tables.secretMaterialsTable) | |
| 25 | − .where(eq(tables.secretMaterialsTable.id, id)) | |
| 26 | − .limit(1); | |
| 27 | − | |
| 28 | − const row = firstOption(rows); | |
| 29 | − return Option.isSome(row) | |
| 30 | − ? Option.some(decodeSecretMaterial(row.value)) | |
| 31 | − : Option.none<SecretMaterial>(); | |
| 32 | − }), | |
| 33 | − | |
| 34 | − listAll: () => | |
| 35 | − client.use("rows.secret_materials.list_all", async (db) => { | |
| 36 | − const rows = await db | |
| 37 | − .select({ | |
| 38 | − id: tables.secretMaterialsTable.id, | |
| 39 | − name: tables.secretMaterialsTable.name, | |
| 40 | − purpose: tables.secretMaterialsTable.purpose, | |
| 41 | − createdAt: tables.secretMaterialsTable.createdAt, | |
| 42 | − updatedAt: tables.secretMaterialsTable.updatedAt, | |
| 43 | − }) | |
| 44 | − .from(tables.secretMaterialsTable) | |
| 45 | − .orderBy(desc(tables.secretMaterialsTable.updatedAt)); | |
| 46 | − | |
| 47 | − return rows; | |
| 48 | − }), | |
| 49 | − | |
| 50 | − upsert: (material: SecretMaterial) => | |
| 51 | − client.use("rows.secret_materials.upsert", async (db) => { | |
| 52 | − await db | |
| 53 | − .insert(tables.secretMaterialsTable) | |
| 54 | − .values(material) | |
| 55 | − .onConflictDoUpdate({ | |
| 56 | − target: [tables.secretMaterialsTable.id], | |
| 57 | − set: { | |
| 58 | − ...withoutCreatedAt(material), | |
| 59 | − }, | |
| 60 | − }); | |
| 61 | − }), | |
| 62 | − | |
| 63 | − updateById: (id: SecretMaterial["id"], update: { name?: string | null; value?: string }) => | |
| 64 | − client.use("rows.secret_materials.update_by_id", async (db) => { | |
| 65 | − const set: Record<string, unknown> = { updatedAt: Date.now() }; | |
| 66 | − if (update.name !== undefined) set.name = update.name; | |
| 67 | − if (update.value !== undefined) set.value = update.value; | |
| 68 | − | |
| 69 | − const updated = await db | |
| 70 | − .update(tables.secretMaterialsTable) | |
| 71 | − .set(set) | |
| 72 | − .where(eq(tables.secretMaterialsTable.id, id)) | |
| 73 | − .returning({ | |
| 74 | − id: tables.secretMaterialsTable.id, | |
| 75 | − name: tables.secretMaterialsTable.name, | |
| 76 | − purpose: tables.secretMaterialsTable.purpose, | |
| 77 | − createdAt: tables.secretMaterialsTable.createdAt, | |
| 78 | − updatedAt: tables.secretMaterialsTable.updatedAt, | |
| 79 | − }); | |
| 80 | − | |
| 81 | − const row = firstOption(updated); | |
| 82 | − return Option.isSome(row) ? Option.some(row.value) : Option.none(); | |
| 83 | − }), | |
| 84 | − | |
| 85 | − removeById: (id: SecretMaterial["id"]) => | |
| 86 | − client.use("rows.secret_materials.remove", async (db) => { | |
| 87 | − const deleted = await db | |
| 88 | − .delete(tables.secretMaterialsTable) | |
| 89 | − .where(eq(tables.secretMaterialsTable.id, id)) | |
| 90 | − .returning(); | |
| 91 | − | |
| 92 | − return deleted.length > 0; | |
| 93 | − }), | |
| 94 | − | |
| 95 | − /** | |
| 96 | − * For each secret, find the sources that reference it via auth artifacts. | |
| 97 | − * Returns a map of secretId -> Array<{ sourceId, sourceName }>. | |
| 98 | − */ | |
| 99 | − listLinkedSources: () => | |
| 100 | − client.use("rows.secret_materials.list_linked_sources", async (db) => { | |
| 101 | − const rows = await db | |
| 102 | − .select({ | |
| 103 | − artifactKind: tables.authArtifactsTable.artifactKind, | |
| 104 | − configJson: tables.authArtifactsTable.configJson, | |
| 105 | − sourceId: tables.authArtifactsTable.sourceId, | |
| 106 | − sourceName: tables.sourcesTable.name, | |
| 107 | − }) | |
| 108 | − .from(tables.authArtifactsTable) | |
| 109 | − .innerJoin( | |
| 110 | − tables.sourcesTable, | |
| 111 | − and( | |
| 112 | − eq(tables.authArtifactsTable.workspaceId, tables.sourcesTable.workspaceId), | |
| 113 | − eq(tables.authArtifactsTable.sourceId, tables.sourcesTable.sourceId), | |
| 114 | − ), | |
| 115 | − ); | |
| 116 | − | |
| 117 | − const result = new Map<string, Array<{ sourceId: string; sourceName: string }>>(); | |
| 118 | − | |
| 119 | − for (const row of rows) { | |
| 120 | − const addLink = (secretId: string) => { | |
| 121 | − let links = result.get(secretId); | |
| 122 | − if (!links) { | |
| 123 | − links = []; | |
| 124 | − result.set(secretId, links); | |
| 125 | − } | |
| 126 | − // Avoid duplicate entries | |
| 127 | − if (!links.some((l) => l.sourceId === row.sourceId)) { | |
| 128 | − links.push({ sourceId: row.sourceId, sourceName: row.sourceName }); | |
| 129 | − } | |
| 130 | − }; | |
| 131 | − | |
| 132 | − for (const ref of authArtifactSecretRefs({ | |
| 133 | − artifactKind: row.artifactKind, | |
| 134 | − configJson: row.configJson, | |
| 135 | − })) { | |
| 136 | − if (ref.providerId === "postgres") { | |
| 137 | − addLink(ref.handle); | |
| 138 | − } | |
| 139 | − } | |
| 140 | − } | |
| 141 | − | |
| 142 | − return result; | |
| 143 | − }), | |
| 144 | −}); | |
| @@ -1,126 +0,0 @@ | ||
| 1 | −import { authArtifactSecretRefs, type AuthArtifact } from "#schema"; | |
| 2 | −import { inArray } from "drizzle-orm"; | |
| 3 | −import * as Option from "effect/Option"; | |
| 4 | − | |
| 5 | −import type { DrizzleClient } from "../client"; | |
| 6 | −import type { DrizzleTables } from "../schema"; | |
| 7 | − | |
| 8 | −export const firstOption = <A>(rows: ReadonlyArray<A>): Option.Option<A> => | |
| 9 | − rows.length > 0 ? Option.some(rows[0] as A) : Option.none<A>(); | |
| 10 | − | |
| 11 | −export const withoutCreatedAt = <A extends { createdAt: unknown }>( | |
| 12 | − value: A, | |
| 13 | −): Omit<A, "createdAt"> => { | |
| 14 | − const { createdAt: _createdAt, ...rest } = value; | |
| 15 | − return rest; | |
| 16 | −}; | |
| 17 | − | |
| 18 | −const POSTGRES_SECRET_PROVIDER_ID = "postgres"; | |
| 19 | − | |
| 20 | −export const postgresSecretHandlesFromAuthArtifacts = ( | |
| 21 | − artifacts: ReadonlyArray<Pick<AuthArtifact, "artifactKind" | "configJson">>, | |
| 22 | −): ReadonlyArray<string> => { | |
| 23 | − const handles = new Set<string>(); | |
| 24 | − | |
| 25 | − for (const artifact of artifacts) { | |
| 26 | − for (const ref of authArtifactSecretRefs(artifact)) { | |
| 27 | − if (ref.providerId === POSTGRES_SECRET_PROVIDER_ID) { | |
| 28 | − handles.add(ref.handle); | |
| 29 | − } | |
| 30 | − } | |
| 31 | − } | |
| 32 | − | |
| 33 | − return [...handles]; | |
| 34 | −}; | |
| 35 | − | |
| 36 | −export const chunkArray = <A>( | |
| 37 | − values: ReadonlyArray<A>, | |
| 38 | − chunkSize: number, | |
| 39 | −): ReadonlyArray<ReadonlyArray<A>> => { | |
| 40 | − if (chunkSize <= 0) { | |
| 41 | − throw new Error(`chunkSize must be positive, received ${chunkSize}`); | |
| 42 | − } | |
| 43 | − | |
| 44 | − const chunks: Array<ReadonlyArray<A>> = []; | |
| 45 | − for (let index = 0; index < values.length; index += chunkSize) { | |
| 46 | − chunks.push(values.slice(index, index + chunkSize)); | |
| 47 | − } | |
| 48 | − | |
| 49 | − return chunks; | |
| 50 | −}; | |
| 51 | − | |
| 52 | −export const cleanupOrphanedSourceRecipes = async (input: { | |
| 53 | − tx: Parameters<Parameters<DrizzleClient["useTx"]>[1]>[0]; | |
| 54 | − tables: DrizzleTables; | |
| 55 | − candidateRecipeIds: ReadonlyArray<string>; | |
| 56 | − candidateRecipeRevisionIds: ReadonlyArray<string>; | |
| 57 | −}) => { | |
| 58 | − const recipeIds = [...new Set(input.candidateRecipeIds)]; | |
| 59 | − const recipeRevisionIds = [...new Set(input.candidateRecipeRevisionIds)]; | |
| 60 | − | |
| 61 | − const remainingRevisionIds = recipeRevisionIds.length > 0 | |
| 62 | − ? ( | |
| 63 | − await input.tx | |
| 64 | − .select({ recipeRevisionId: input.tables.sourcesTable.recipeRevisionId }) | |
| 65 | − .from(input.tables.sourcesTable) | |
| 66 | − .where(inArray(input.tables.sourcesTable.recipeRevisionId, recipeRevisionIds)) | |
| 67 | − ).map((row) => row.recipeRevisionId) | |
| 68 | − : []; | |
| 69 | − const orphanRevisionIds = recipeRevisionIds.filter( | |
| 70 | − (recipeRevisionId) => !remainingRevisionIds.includes(recipeRevisionId), | |
| 71 | − ); | |
| 72 | − | |
| 73 | − if (orphanRevisionIds.length > 0) { | |
| 74 | − await input.tx | |
| 75 | − .delete(input.tables.sourceRecipeDocumentsTable) | |
| 76 | − .where(inArray(input.tables.sourceRecipeDocumentsTable.recipeRevisionId, orphanRevisionIds)); | |
| 77 | − await input.tx | |
| 78 | − .delete(input.tables.sourceRecipeSchemaBundlesTable) | |
| 79 | − .where(inArray(input.tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, orphanRevisionIds)); | |
| 80 | − await input.tx | |
| 81 | − .delete(input.tables.sourceRecipeOperationsTable) | |
| 82 | − .where(inArray(input.tables.sourceRecipeOperationsTable.recipeRevisionId, orphanRevisionIds)); | |
| 83 | − } | |
| 84 | − | |
| 85 | − const remainingRecipeIds = recipeIds.length > 0 | |
| 86 | − ? ( | |
| 87 | − await input.tx | |
| 88 | − .select({ recipeId: input.tables.sourcesTable.recipeId }) | |
| 89 | − .from(input.tables.sourcesTable) | |
| 90 | − .where(inArray(input.tables.sourcesTable.recipeId, recipeIds)) | |
| 91 | − ).map((row) => row.recipeId) | |
| 92 | − : []; | |
| 93 | − const orphanRecipeIds = recipeIds.filter( | |
| 94 | − (recipeId) => !remainingRecipeIds.includes(recipeId), | |
| 95 | − ); | |
| 96 | − | |
| 97 | − if (orphanRecipeIds.length === 0) { | |
| 98 | − return; | |
| 99 | − } | |
| 100 | − | |
| 101 | − const orphanRecipeRevisionIds = ( | |
| 102 | − await input.tx | |
| 103 | − .select({ id: input.tables.sourceRecipeRevisionsTable.id }) | |
| 104 | − .from(input.tables.sourceRecipeRevisionsTable) | |
| 105 | − .where(inArray(input.tables.sourceRecipeRevisionsTable.recipeId, orphanRecipeIds)) | |
| 106 | − ).map((revision) => revision.id); | |
| 107 | − | |
| 108 | − if (orphanRecipeRevisionIds.length > 0) { | |
| 109 | − await input.tx | |
| 110 | − .delete(input.tables.sourceRecipeDocumentsTable) | |
| 111 | − .where(inArray(input.tables.sourceRecipeDocumentsTable.recipeRevisionId, orphanRecipeRevisionIds)); | |
| 112 | − await input.tx | |
| 113 | − .delete(input.tables.sourceRecipeSchemaBundlesTable) | |
| 114 | − .where(inArray(input.tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, orphanRecipeRevisionIds)); | |
| 115 | − await input.tx | |
| 116 | − .delete(input.tables.sourceRecipeOperationsTable) | |
| 117 | − .where(inArray(input.tables.sourceRecipeOperationsTable.recipeRevisionId, orphanRecipeRevisionIds)); | |
| 118 | − } | |
| 119 | − | |
| 120 | − await input.tx | |
| 121 | − .delete(input.tables.sourceRecipeRevisionsTable) | |
| 122 | − .where(inArray(input.tables.sourceRecipeRevisionsTable.recipeId, orphanRecipeIds)); | |
| 123 | − await input.tx | |
| 124 | − .delete(input.tables.sourceRecipesTable) | |
| 125 | − .where(inArray(input.tables.sourceRecipesTable.id, orphanRecipeIds)); | |
| 126 | −}; | |
| @@ -1,163 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type SourceAuthSession, | |
| 3 | − SourceAuthSessionSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, asc, eq, isNull } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeSourceAuthSession = Schema.decodeUnknownSync(SourceAuthSessionSchema); | |
| 14 | − | |
| 15 | −type Mutable<T> = { -readonly [K in keyof T]: T[K] }; | |
| 16 | −type SourceAuthSessionPatch = Partial< | |
| 17 | − Omit<Mutable<SourceAuthSession>, "id" | "workspaceId" | "sourceId" | "createdAt"> | |
| 18 | −>; | |
| 19 | − | |
| 20 | −export const createSourceAuthSessionsRepo = ( | |
| 21 | − client: DrizzleClient, | |
| 22 | − tables: DrizzleTables, | |
| 23 | −) => ({ | |
| 24 | − listAll: () => | |
| 25 | − client.use("rows.source_auth_sessions.list_all", async (db) => { | |
| 26 | − const rows = await db | |
| 27 | − .select() | |
| 28 | − .from(tables.sourceAuthSessionsTable) | |
| 29 | − .orderBy(asc(tables.sourceAuthSessionsTable.updatedAt), asc(tables.sourceAuthSessionsTable.id)); | |
| 30 | − | |
| 31 | − return rows.map((row) => decodeSourceAuthSession(row)); | |
| 32 | − }), | |
| 33 | − | |
| 34 | − listByWorkspaceId: (workspaceId: SourceAuthSession["workspaceId"]) => | |
| 35 | − client.use("rows.source_auth_sessions.list_by_workspace", async (db) => { | |
| 36 | − const rows = await db | |
| 37 | − .select() | |
| 38 | − .from(tables.sourceAuthSessionsTable) | |
| 39 | − .where(eq(tables.sourceAuthSessionsTable.workspaceId, workspaceId)) | |
| 40 | − .orderBy(asc(tables.sourceAuthSessionsTable.updatedAt), asc(tables.sourceAuthSessionsTable.id)); | |
| 41 | − | |
| 42 | − return rows.map((row) => decodeSourceAuthSession(row)); | |
| 43 | − }), | |
| 44 | − | |
| 45 | − getById: (id: SourceAuthSession["id"]) => | |
| 46 | − client.use("rows.source_auth_sessions.get_by_id", async (db) => { | |
| 47 | − const rows = await db | |
| 48 | − .select() | |
| 49 | − .from(tables.sourceAuthSessionsTable) | |
| 50 | − .where(eq(tables.sourceAuthSessionsTable.id, id)) | |
| 51 | − .limit(1); | |
| 52 | − | |
| 53 | − const row = firstOption(rows); | |
| 54 | − return Option.isSome(row) | |
| 55 | − ? Option.some(decodeSourceAuthSession(row.value)) | |
| 56 | − : Option.none<SourceAuthSession>(); | |
| 57 | − }), | |
| 58 | − | |
| 59 | − getByState: (state: SourceAuthSession["state"]) => | |
| 60 | − client.use("rows.source_auth_sessions.get_by_state", async (db) => { | |
| 61 | − const rows = await db | |
| 62 | − .select() | |
| 63 | − .from(tables.sourceAuthSessionsTable) | |
| 64 | − .where(eq(tables.sourceAuthSessionsTable.state, state)) | |
| 65 | − .limit(1); | |
| 66 | − | |
| 67 | − const row = firstOption(rows); | |
| 68 | − return Option.isSome(row) | |
| 69 | − ? Option.some(decodeSourceAuthSession(row.value)) | |
| 70 | − : Option.none<SourceAuthSession>(); | |
| 71 | − }), | |
| 72 | − | |
| 73 | − getPendingByWorkspaceSourceAndActor: (input: { | |
| 74 | − workspaceId: SourceAuthSession["workspaceId"]; | |
| 75 | − sourceId: SourceAuthSession["sourceId"]; | |
| 76 | − actorAccountId: SourceAuthSession["actorAccountId"]; | |
| 77 | − credentialSlot?: SourceAuthSession["credentialSlot"]; | |
| 78 | − }) => | |
| 79 | − client.use( | |
| 80 | − "rows.source_auth_sessions.get_pending_by_workspace_source_actor", | |
| 81 | − async (db) => { | |
| 82 | − const rows = await db | |
| 83 | − .select() | |
| 84 | − .from(tables.sourceAuthSessionsTable) | |
| 85 | − .where( | |
| 86 | − and( | |
| 87 | − eq(tables.sourceAuthSessionsTable.workspaceId, input.workspaceId), | |
| 88 | − eq(tables.sourceAuthSessionsTable.sourceId, input.sourceId), | |
| 89 | − input.credentialSlot === undefined | |
| 90 | − ? undefined | |
| 91 | − : eq(tables.sourceAuthSessionsTable.credentialSlot, input.credentialSlot), | |
| 92 | − input.actorAccountId === null | |
| 93 | − ? isNull(tables.sourceAuthSessionsTable.actorAccountId) | |
| 94 | − : eq(tables.sourceAuthSessionsTable.actorAccountId, input.actorAccountId), | |
| 95 | − eq(tables.sourceAuthSessionsTable.status, "pending"), | |
| 96 | − ), | |
| 97 | − ) | |
| 98 | − .orderBy( | |
| 99 | − asc(tables.sourceAuthSessionsTable.updatedAt), | |
| 100 | − asc(tables.sourceAuthSessionsTable.id), | |
| 101 | − ) | |
| 102 | − .limit(1); | |
| 103 | − | |
| 104 | − const row = firstOption(rows); | |
| 105 | − return Option.isSome(row) | |
| 106 | − ? Option.some(decodeSourceAuthSession(row.value)) | |
| 107 | − : Option.none<SourceAuthSession>(); | |
| 108 | − }, | |
| 109 | − ), | |
| 110 | − | |
| 111 | − insert: (session: SourceAuthSession) => | |
| 112 | − client.use("rows.source_auth_sessions.insert", async (db) => { | |
| 113 | − await db.insert(tables.sourceAuthSessionsTable).values(session); | |
| 114 | − }), | |
| 115 | − | |
| 116 | − update: ( | |
| 117 | − id: SourceAuthSession["id"], | |
| 118 | − patch: SourceAuthSessionPatch, | |
| 119 | − ) => | |
| 120 | − client.use("rows.source_auth_sessions.update", async (db) => { | |
| 121 | − const rows = await db | |
| 122 | − .update(tables.sourceAuthSessionsTable) | |
| 123 | − .set(patch) | |
| 124 | − .where(eq(tables.sourceAuthSessionsTable.id, id)) | |
| 125 | − .returning(); | |
| 126 | − | |
| 127 | − const row = firstOption(rows); | |
| 128 | − return Option.isSome(row) | |
| 129 | − ? Option.some(decodeSourceAuthSession(row.value)) | |
| 130 | − : Option.none<SourceAuthSession>(); | |
| 131 | − }), | |
| 132 | − | |
| 133 | − upsert: (session: SourceAuthSession) => | |
| 134 | − client.use("rows.source_auth_sessions.upsert", async (db) => { | |
| 135 | − await db | |
| 136 | − .insert(tables.sourceAuthSessionsTable) | |
| 137 | − .values(session) | |
| 138 | − .onConflictDoUpdate({ | |
| 139 | − target: [tables.sourceAuthSessionsTable.id], | |
| 140 | − set: { | |
| 141 | − ...withoutCreatedAt(session), | |
| 142 | − }, | |
| 143 | − }); | |
| 144 | − }), | |
| 145 | − | |
| 146 | − removeByWorkspaceAndSourceId: ( | |
| 147 | − workspaceId: SourceAuthSession["workspaceId"], | |
| 148 | − sourceId: SourceAuthSession["sourceId"], | |
| 149 | − ) => | |
| 150 | − client.use("rows.source_auth_sessions.remove_by_workspace_source", async (db) => { | |
| 151 | − const deleted = await db | |
| 152 | − .delete(tables.sourceAuthSessionsTable) | |
| 153 | − .where( | |
| 154 | − and( | |
| 155 | − eq(tables.sourceAuthSessionsTable.workspaceId, workspaceId), | |
| 156 | − eq(tables.sourceAuthSessionsTable.sourceId, sourceId), | |
| 157 | − ), | |
| 158 | − ) | |
| 159 | − .returning(); | |
| 160 | − | |
| 161 | − return deleted.length > 0; | |
| 162 | − }), | |
| 163 | −}); | |
| @@ -1,79 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type WorkspaceSourceOauthClient, | |
| 3 | − WorkspaceSourceOauthClientSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, eq } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeWorkspaceSourceOauthClient = Schema.decodeUnknownSync( | |
| 14 | − WorkspaceSourceOauthClientSchema, | |
| 15 | −); | |
| 16 | − | |
| 17 | −export const createSourceOauthClientsRepo = ( | |
| 18 | − client: DrizzleClient, | |
| 19 | − tables: DrizzleTables, | |
| 20 | −) => ({ | |
| 21 | − getByWorkspaceSourceAndProvider: (input: { | |
| 22 | − workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 23 | − sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 24 | − providerKey: string; | |
| 25 | − }) => | |
| 26 | − client.use("rows.source_oauth_clients.get_by_workspace_source_provider", async (db) => { | |
| 27 | − const rows = await db | |
| 28 | − .select() | |
| 29 | − .from(tables.workspaceSourceOauthClientsTable) | |
| 30 | − .where( | |
| 31 | − and( | |
| 32 | − eq(tables.workspaceSourceOauthClientsTable.workspaceId, input.workspaceId), | |
| 33 | − eq(tables.workspaceSourceOauthClientsTable.sourceId, input.sourceId), | |
| 34 | − eq(tables.workspaceSourceOauthClientsTable.providerKey, input.providerKey), | |
| 35 | − ), | |
| 36 | − ) | |
| 37 | − .limit(1); | |
| 38 | − | |
| 39 | − const row = firstOption(rows); | |
| 40 | − return Option.isSome(row) | |
| 41 | − ? Option.some(decodeWorkspaceSourceOauthClient(row.value)) | |
| 42 | − : Option.none<WorkspaceSourceOauthClient>(); | |
| 43 | − }), | |
| 44 | − | |
| 45 | − upsert: (oauthClient: WorkspaceSourceOauthClient) => | |
| 46 | − client.use("rows.source_oauth_clients.upsert", async (db) => { | |
| 47 | − await db | |
| 48 | − .insert(tables.workspaceSourceOauthClientsTable) | |
| 49 | − .values(oauthClient) | |
| 50 | − .onConflictDoUpdate({ | |
| 51 | − target: [ | |
| 52 | − tables.workspaceSourceOauthClientsTable.workspaceId, | |
| 53 | − tables.workspaceSourceOauthClientsTable.sourceId, | |
| 54 | − tables.workspaceSourceOauthClientsTable.providerKey, | |
| 55 | − ], | |
| 56 | − set: { | |
| 57 | − ...withoutCreatedAt(oauthClient), | |
| 58 | − }, | |
| 59 | − }); | |
| 60 | − }), | |
| 61 | − | |
| 62 | − removeByWorkspaceAndSourceId: (input: { | |
| 63 | − workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 64 | − sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 65 | − }) => | |
| 66 | − client.use("rows.source_oauth_clients.remove_by_workspace_source", async (db) => { | |
| 67 | − const deleted = await db | |
| 68 | − .delete(tables.workspaceSourceOauthClientsTable) | |
| 69 | − .where( | |
| 70 | − and( | |
| 71 | − eq(tables.workspaceSourceOauthClientsTable.workspaceId, input.workspaceId), | |
| 72 | − eq(tables.workspaceSourceOauthClientsTable.sourceId, input.sourceId), | |
| 73 | − ), | |
| 74 | − ) | |
| 75 | − .returning(); | |
| 76 | − | |
| 77 | − return deleted.length; | |
| 78 | − }), | |
| 79 | −}); | |
| @@ -1,85 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecipeDocumentRecord, | |
| 3 | − StoredSourceRecipeDocumentRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import { Schema } from "effect"; | |
| 6 | −import { asc, eq, inArray } from "drizzle-orm"; | |
| 7 | − | |
| 8 | −import type { DrizzleClient } from "../client"; | |
| 9 | −import type { DrizzleTables } from "../schema"; | |
| 10 | −import { chunkArray } from "./shared"; | |
| 11 | − | |
| 12 | −const decodeStoredSourceRecipeDocumentRecord = Schema.decodeUnknownSync( | |
| 13 | − StoredSourceRecipeDocumentRecordSchema, | |
| 14 | −); | |
| 15 | − | |
| 16 | −const RECIPE_DOCUMENT_INSERT_BATCH_SIZE = 500; | |
| 17 | − | |
| 18 | −export const createSourceRecipeDocumentsRepo = ( | |
| 19 | − client: DrizzleClient, | |
| 20 | − tables: DrizzleTables, | |
| 21 | −) => ({ | |
| 22 | − listByRevisionId: (recipeRevisionId: StoredSourceRecipeDocumentRecord["recipeRevisionId"]) => | |
| 23 | − client.use("rows.source_recipe_documents.list_by_revision", async (db) => { | |
| 24 | − const rows = await db | |
| 25 | − .select() | |
| 26 | − .from(tables.sourceRecipeDocumentsTable) | |
| 27 | − .where(eq(tables.sourceRecipeDocumentsTable.recipeRevisionId, recipeRevisionId)) | |
| 28 | − .orderBy( | |
| 29 | − asc(tables.sourceRecipeDocumentsTable.documentKind), | |
| 30 | − asc(tables.sourceRecipeDocumentsTable.documentKey), | |
| 31 | − ); | |
| 32 | − | |
| 33 | − return rows.map((row) => decodeStoredSourceRecipeDocumentRecord(row)); | |
| 34 | − }), | |
| 35 | − | |
| 36 | − listByRevisionIds: ( | |
| 37 | − recipeRevisionIds: readonly StoredSourceRecipeDocumentRecord["recipeRevisionId"][], | |
| 38 | − ) => | |
| 39 | − client.use("rows.source_recipe_documents.list_by_revisions", async (db) => { | |
| 40 | − if (recipeRevisionIds.length === 0) { | |
| 41 | − return [] as StoredSourceRecipeDocumentRecord[]; | |
| 42 | − } | |
| 43 | − | |
| 44 | − const rows = await db | |
| 45 | − .select() | |
| 46 | − .from(tables.sourceRecipeDocumentsTable) | |
| 47 | − .where(inArray(tables.sourceRecipeDocumentsTable.recipeRevisionId, [...recipeRevisionIds])) | |
| 48 | − .orderBy( | |
| 49 | − asc(tables.sourceRecipeDocumentsTable.recipeRevisionId), | |
| 50 | − asc(tables.sourceRecipeDocumentsTable.documentKind), | |
| 51 | − asc(tables.sourceRecipeDocumentsTable.documentKey), | |
| 52 | − ); | |
| 53 | − | |
| 54 | − return rows.map((row) => decodeStoredSourceRecipeDocumentRecord(row)); | |
| 55 | − }), | |
| 56 | − | |
| 57 | − replaceForRevision: (input: { | |
| 58 | − recipeRevisionId: StoredSourceRecipeDocumentRecord["recipeRevisionId"]; | |
| 59 | − documents: readonly StoredSourceRecipeDocumentRecord[]; | |
| 60 | − }) => | |
| 61 | − client.useTx("rows.source_recipe_documents.replace_for_revision", async (tx) => { | |
| 62 | − await tx | |
| 63 | − .delete(tables.sourceRecipeDocumentsTable) | |
| 64 | − .where(eq(tables.sourceRecipeDocumentsTable.recipeRevisionId, input.recipeRevisionId)); | |
| 65 | − | |
| 66 | − if (input.documents.length > 0) { | |
| 67 | − for (const batch of chunkArray( | |
| 68 | − input.documents, | |
| 69 | − RECIPE_DOCUMENT_INSERT_BATCH_SIZE, | |
| 70 | − )) { | |
| 71 | − await tx.insert(tables.sourceRecipeDocumentsTable).values([...batch]); | |
| 72 | − } | |
| 73 | − } | |
| 74 | − }), | |
| 75 | − | |
| 76 | − removeByRevisionId: (recipeRevisionId: StoredSourceRecipeDocumentRecord["recipeRevisionId"]) => | |
| 77 | − client.use("rows.source_recipe_documents.remove_by_revision", async (db) => { | |
| 78 | − const deleted = await db | |
| 79 | − .delete(tables.sourceRecipeDocumentsTable) | |
| 80 | − .where(eq(tables.sourceRecipeDocumentsTable.recipeRevisionId, recipeRevisionId)) | |
| 81 | − .returning(); | |
| 82 | − | |
| 83 | − return deleted.length; | |
| 84 | − }), | |
| 85 | −}); | |
| @@ -1,86 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecipeOperationRecord, | |
| 3 | − StoredSourceRecipeOperationRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import { Schema } from "effect"; | |
| 6 | −import { asc, eq, inArray } from "drizzle-orm"; | |
| 7 | − | |
| 8 | −import type { DrizzleClient } from "../client"; | |
| 9 | −import type { DrizzleTables } from "../schema"; | |
| 10 | −import { chunkArray } from "./shared"; | |
| 11 | − | |
| 12 | −const decodeStoredSourceRecipeOperationRecord = Schema.decodeUnknownSync( | |
| 13 | − StoredSourceRecipeOperationRecordSchema, | |
| 14 | −); | |
| 15 | − | |
| 16 | −// Keep wide recipe-operation inserts well below Postgres/PGlite parameter limits. | |
| 17 | −const RECIPE_OPERATION_INSERT_BATCH_SIZE = 200; | |
| 18 | − | |
| 19 | −export const createSourceRecipeOperationsRepo = ( | |
| 20 | − client: DrizzleClient, | |
| 21 | − tables: DrizzleTables, | |
| 22 | −) => ({ | |
| 23 | − listByRevisionId: (recipeRevisionId: StoredSourceRecipeOperationRecord["recipeRevisionId"]) => | |
| 24 | − client.use("rows.source_recipe_operations.list_by_revision", async (db) => { | |
| 25 | − const rows = await db | |
| 26 | − .select() | |
| 27 | − .from(tables.sourceRecipeOperationsTable) | |
| 28 | − .where(eq(tables.sourceRecipeOperationsTable.recipeRevisionId, recipeRevisionId)) | |
| 29 | − .orderBy( | |
| 30 | − asc(tables.sourceRecipeOperationsTable.toolId), | |
| 31 | − asc(tables.sourceRecipeOperationsTable.id), | |
| 32 | − ); | |
| 33 | − | |
| 34 | − return rows.map((row) => decodeStoredSourceRecipeOperationRecord(row)); | |
| 35 | − }), | |
| 36 | − | |
| 37 | − listByRevisionIds: ( | |
| 38 | − recipeRevisionIds: readonly StoredSourceRecipeOperationRecord["recipeRevisionId"][], | |
| 39 | − ) => | |
| 40 | − client.use("rows.source_recipe_operations.list_by_revisions", async (db) => { | |
| 41 | − if (recipeRevisionIds.length === 0) { | |
| 42 | − return [] as StoredSourceRecipeOperationRecord[]; | |
| 43 | − } | |
| 44 | − | |
| 45 | − const rows = await db | |
| 46 | − .select() | |
| 47 | − .from(tables.sourceRecipeOperationsTable) | |
| 48 | − .where(inArray(tables.sourceRecipeOperationsTable.recipeRevisionId, [...recipeRevisionIds])) | |
| 49 | − .orderBy( | |
| 50 | − asc(tables.sourceRecipeOperationsTable.recipeRevisionId), | |
| 51 | − asc(tables.sourceRecipeOperationsTable.toolId), | |
| 52 | − asc(tables.sourceRecipeOperationsTable.id), | |
| 53 | − ); | |
| 54 | − | |
| 55 | − return rows.map((row) => decodeStoredSourceRecipeOperationRecord(row)); | |
| 56 | − }), | |
| 57 | − | |
| 58 | − replaceForRevision: (input: { | |
| 59 | − recipeRevisionId: StoredSourceRecipeOperationRecord["recipeRevisionId"]; | |
| 60 | − operations: readonly StoredSourceRecipeOperationRecord[]; | |
| 61 | − }) => | |
| 62 | − client.useTx("rows.source_recipe_operations.replace_for_revision", async (tx) => { | |
| 63 | − await tx | |
| 64 | − .delete(tables.sourceRecipeOperationsTable) | |
| 65 | − .where(eq(tables.sourceRecipeOperationsTable.recipeRevisionId, input.recipeRevisionId)); | |
| 66 | − | |
| 67 | − if (input.operations.length > 0) { | |
| 68 | − for (const batch of chunkArray( | |
| 69 | − input.operations, | |
| 70 | − RECIPE_OPERATION_INSERT_BATCH_SIZE, | |
| 71 | − )) { | |
| 72 | − await tx.insert(tables.sourceRecipeOperationsTable).values([...batch]); | |
| 73 | − } | |
| 74 | − } | |
| 75 | − }), | |
| 76 | − | |
| 77 | − removeByRevisionId: (recipeRevisionId: StoredSourceRecipeOperationRecord["recipeRevisionId"]) => | |
| 78 | − client.use("rows.source_recipe_operations.remove_by_revision", async (db) => { | |
| 79 | − const deleted = await db | |
| 80 | − .delete(tables.sourceRecipeOperationsTable) | |
| 81 | − .where(eq(tables.sourceRecipeOperationsTable.recipeRevisionId, recipeRevisionId)) | |
| 82 | − .returning(); | |
| 83 | − | |
| 84 | − return deleted.length; | |
| 85 | − }), | |
| 86 | −}); | |
| @@ -1,157 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecipeRevisionRecord, | |
| 3 | − StoredSourceRecipeRevisionRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { asc, desc, eq, inArray, and } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeStoredSourceRecipeRevisionRecord = Schema.decodeUnknownSync( | |
| 14 | − StoredSourceRecipeRevisionRecordSchema, | |
| 15 | −); | |
| 16 | − | |
| 17 | −type Mutable<T> = { -readonly [K in keyof T]: T[K] }; | |
| 18 | −type RecipeRevisionPatch = Partial< | |
| 19 | − Omit<Mutable<StoredSourceRecipeRevisionRecord>, "id" | "recipeId" | "revisionNumber" | "createdAt"> | |
| 20 | −>; | |
| 21 | − | |
| 22 | −export const createSourceRecipeRevisionsRepo = ( | |
| 23 | − client: DrizzleClient, | |
| 24 | − tables: DrizzleTables, | |
| 25 | −) => ({ | |
| 26 | − listByRecipeId: (recipeId: StoredSourceRecipeRevisionRecord["recipeId"]) => | |
| 27 | − client.use("rows.source_recipe_revisions.list_by_recipe", async (db) => { | |
| 28 | − const rows = await db | |
| 29 | − .select() | |
| 30 | − .from(tables.sourceRecipeRevisionsTable) | |
| 31 | − .where(eq(tables.sourceRecipeRevisionsTable.recipeId, recipeId)) | |
| 32 | − .orderBy( | |
| 33 | − asc(tables.sourceRecipeRevisionsTable.revisionNumber), | |
| 34 | − asc(tables.sourceRecipeRevisionsTable.createdAt), | |
| 35 | − ); | |
| 36 | − | |
| 37 | − return rows.map((row) => decodeStoredSourceRecipeRevisionRecord(row)); | |
| 38 | − }), | |
| 39 | − | |
| 40 | − listByIds: (ids: readonly StoredSourceRecipeRevisionRecord["id"][]) => | |
| 41 | − client.use("rows.source_recipe_revisions.list_by_ids", async (db) => { | |
| 42 | − if (ids.length === 0) { | |
| 43 | − return [] as StoredSourceRecipeRevisionRecord[]; | |
| 44 | − } | |
| 45 | − | |
| 46 | − const rows = await db | |
| 47 | − .select() | |
| 48 | − .from(tables.sourceRecipeRevisionsTable) | |
| 49 | − .where(inArray(tables.sourceRecipeRevisionsTable.id, [...ids])) | |
| 50 | − .orderBy( | |
| 51 | − asc(tables.sourceRecipeRevisionsTable.recipeId), | |
| 52 | − asc(tables.sourceRecipeRevisionsTable.revisionNumber), | |
| 53 | − ); | |
| 54 | − | |
| 55 | − return rows.map((row) => decodeStoredSourceRecipeRevisionRecord(row)); | |
| 56 | − }), | |
| 57 | − | |
| 58 | − getById: (id: StoredSourceRecipeRevisionRecord["id"]) => | |
| 59 | − client.use("rows.source_recipe_revisions.get_by_id", async (db) => { | |
| 60 | − const rows = await db | |
| 61 | − .select() | |
| 62 | − .from(tables.sourceRecipeRevisionsTable) | |
| 63 | − .where(eq(tables.sourceRecipeRevisionsTable.id, id)) | |
| 64 | − .limit(1); | |
| 65 | − | |
| 66 | − const row = firstOption(rows); | |
| 67 | − return Option.isSome(row) | |
| 68 | − ? Option.some(decodeStoredSourceRecipeRevisionRecord(row.value)) | |
| 69 | − : Option.none<StoredSourceRecipeRevisionRecord>(); | |
| 70 | − }), | |
| 71 | − | |
| 72 | − getByRecipeAndMaterializationHash: (input: { | |
| 73 | − recipeId: StoredSourceRecipeRevisionRecord["recipeId"]; | |
| 74 | − materializationHash: string; | |
| 75 | − }) => | |
| 76 | − client.use("rows.source_recipe_revisions.get_by_recipe_materialization", async (db) => { | |
| 77 | − const rows = await db | |
| 78 | − .select() | |
| 79 | − .from(tables.sourceRecipeRevisionsTable) | |
| 80 | − .where( | |
| 81 | − and( | |
| 82 | − eq(tables.sourceRecipeRevisionsTable.recipeId, input.recipeId), | |
| 83 | − eq(tables.sourceRecipeRevisionsTable.materializationHash, input.materializationHash), | |
| 84 | − ), | |
| 85 | − ) | |
| 86 | − .limit(1); | |
| 87 | − | |
| 88 | − const row = firstOption(rows); | |
| 89 | − return Option.isSome(row) | |
| 90 | − ? Option.some(decodeStoredSourceRecipeRevisionRecord(row.value)) | |
| 91 | − : Option.none<StoredSourceRecipeRevisionRecord>(); | |
| 92 | − }), | |
| 93 | − | |
| 94 | − nextRevisionNumber: (recipeId: StoredSourceRecipeRevisionRecord["recipeId"]) => | |
| 95 | − client.use("rows.source_recipe_revisions.next_revision_number", async (db) => { | |
| 96 | − const rows = await db | |
| 97 | − .select({ | |
| 98 | − revisionNumber: tables.sourceRecipeRevisionsTable.revisionNumber, | |
| 99 | − }) | |
| 100 | − .from(tables.sourceRecipeRevisionsTable) | |
| 101 | − .where(eq(tables.sourceRecipeRevisionsTable.recipeId, recipeId)) | |
| 102 | − .orderBy(desc(tables.sourceRecipeRevisionsTable.revisionNumber)) | |
| 103 | − .limit(1); | |
| 104 | − | |
| 105 | − return Number(rows[0]?.revisionNumber ?? 0) + 1; | |
| 106 | − }), | |
| 107 | − | |
| 108 | − upsert: (revision: StoredSourceRecipeRevisionRecord) => | |
| 109 | − client.use("rows.source_recipe_revisions.upsert", async (db) => { | |
| 110 | − await db | |
| 111 | − .insert(tables.sourceRecipeRevisionsTable) | |
| 112 | − .values(revision) | |
| 113 | − .onConflictDoUpdate({ | |
| 114 | − target: [tables.sourceRecipeRevisionsTable.id], | |
| 115 | − set: { | |
| 116 | − ...withoutCreatedAt(revision), | |
| 117 | − }, | |
| 118 | − }); | |
| 119 | − }), | |
| 120 | − | |
| 121 | − update: ( | |
| 122 | − id: StoredSourceRecipeRevisionRecord["id"], | |
| 123 | − patch: RecipeRevisionPatch, | |
| 124 | − ) => | |
| 125 | − client.use("rows.source_recipe_revisions.update", async (db) => { | |
| 126 | − const rows = await db | |
| 127 | − .update(tables.sourceRecipeRevisionsTable) | |
| 128 | − .set(patch) | |
| 129 | − .where(eq(tables.sourceRecipeRevisionsTable.id, id)) | |
| 130 | − .returning(); | |
| 131 | − | |
| 132 | − const row = firstOption(rows); | |
| 133 | − return Option.isSome(row) | |
| 134 | − ? Option.some(decodeStoredSourceRecipeRevisionRecord(row.value)) | |
| 135 | − : Option.none<StoredSourceRecipeRevisionRecord>(); | |
| 136 | − }), | |
| 137 | − | |
| 138 | − removeByRecipeId: (recipeId: StoredSourceRecipeRevisionRecord["recipeId"]) => | |
| 139 | − client.use("rows.source_recipe_revisions.remove_by_recipe", async (db) => { | |
| 140 | − const deleted = await db | |
| 141 | − .delete(tables.sourceRecipeRevisionsTable) | |
| 142 | − .where(eq(tables.sourceRecipeRevisionsTable.recipeId, recipeId)) | |
| 143 | − .returning(); | |
| 144 | − | |
| 145 | − return deleted.length; | |
| 146 | − }), | |
| 147 | − | |
| 148 | − removeById: (id: StoredSourceRecipeRevisionRecord["id"]) => | |
| 149 | − client.use("rows.source_recipe_revisions.remove_by_id", async (db) => { | |
| 150 | − const deleted = await db | |
| 151 | − .delete(tables.sourceRecipeRevisionsTable) | |
| 152 | − .where(eq(tables.sourceRecipeRevisionsTable.id, id)) | |
| 153 | − .returning(); | |
| 154 | − | |
| 155 | − return deleted.length > 0; | |
| 156 | − }), | |
| 157 | −}); | |
| @@ -1,106 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecipeSchemaBundleRecord, | |
| 3 | − StoredSourceRecipeSchemaBundleRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { asc, eq, inArray } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeStoredSourceRecipeSchemaBundleRecord = Schema.decodeUnknownSync( | |
| 14 | − StoredSourceRecipeSchemaBundleRecordSchema, | |
| 15 | −); | |
| 16 | − | |
| 17 | −export const createSourceRecipeSchemaBundlesRepo = ( | |
| 18 | − client: DrizzleClient, | |
| 19 | − tables: DrizzleTables, | |
| 20 | −) => ({ | |
| 21 | − getById: (id: StoredSourceRecipeSchemaBundleRecord["id"]) => | |
| 22 | − client.use("rows.source_recipe_schema_bundles.get_by_id", async (db) => { | |
| 23 | − const rows = await db | |
| 24 | − .select() | |
| 25 | − .from(tables.sourceRecipeSchemaBundlesTable) | |
| 26 | − .where(eq(tables.sourceRecipeSchemaBundlesTable.id, id)) | |
| 27 | − .limit(1); | |
| 28 | − | |
| 29 | − const row = firstOption(rows); | |
| 30 | − return Option.isSome(row) | |
| 31 | − ? Option.some(decodeStoredSourceRecipeSchemaBundleRecord(row.value)) | |
| 32 | − : Option.none<StoredSourceRecipeSchemaBundleRecord>(); | |
| 33 | − }), | |
| 34 | − | |
| 35 | − listByRevisionId: (recipeRevisionId: StoredSourceRecipeSchemaBundleRecord["recipeRevisionId"]) => | |
| 36 | − client.use("rows.source_recipe_schema_bundles.list_by_revision", async (db) => { | |
| 37 | − const rows = await db | |
| 38 | − .select() | |
| 39 | − .from(tables.sourceRecipeSchemaBundlesTable) | |
| 40 | − .where(eq(tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, recipeRevisionId)) | |
| 41 | − .orderBy( | |
| 42 | − asc(tables.sourceRecipeSchemaBundlesTable.bundleKind), | |
| 43 | − asc(tables.sourceRecipeSchemaBundlesTable.createdAt), | |
| 44 | − ); | |
| 45 | − | |
| 46 | − return rows.map((row) => decodeStoredSourceRecipeSchemaBundleRecord(row)); | |
| 47 | − }), | |
| 48 | − | |
| 49 | − listByRevisionIds: ( | |
| 50 | − recipeRevisionIds: readonly StoredSourceRecipeSchemaBundleRecord["recipeRevisionId"][], | |
| 51 | − ) => | |
| 52 | − client.use("rows.source_recipe_schema_bundles.list_by_revisions", async (db) => { | |
| 53 | − if (recipeRevisionIds.length === 0) { | |
| 54 | − return [] as StoredSourceRecipeSchemaBundleRecord[]; | |
| 55 | − } | |
| 56 | − | |
| 57 | − const rows = await db | |
| 58 | − .select() | |
| 59 | − .from(tables.sourceRecipeSchemaBundlesTable) | |
| 60 | − .where(inArray(tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, [...recipeRevisionIds])) | |
| 61 | − .orderBy( | |
| 62 | − asc(tables.sourceRecipeSchemaBundlesTable.recipeRevisionId), | |
| 63 | − asc(tables.sourceRecipeSchemaBundlesTable.bundleKind), | |
| 64 | − asc(tables.sourceRecipeSchemaBundlesTable.createdAt), | |
| 65 | − ); | |
| 66 | − | |
| 67 | − return rows.map((row) => decodeStoredSourceRecipeSchemaBundleRecord(row)); | |
| 68 | − }), | |
| 69 | − | |
| 70 | − replaceForRevision: (input: { | |
| 71 | − recipeRevisionId: StoredSourceRecipeSchemaBundleRecord["recipeRevisionId"]; | |
| 72 | − bundles: readonly StoredSourceRecipeSchemaBundleRecord[]; | |
| 73 | − }) => | |
| 74 | − client.useTx("rows.source_recipe_schema_bundles.replace_for_revision", async (tx) => { | |
| 75 | − await tx | |
| 76 | − .delete(tables.sourceRecipeSchemaBundlesTable) | |
| 77 | − .where(eq(tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, input.recipeRevisionId)); | |
| 78 | − | |
| 79 | − if (input.bundles.length > 0) { | |
| 80 | − await tx.insert(tables.sourceRecipeSchemaBundlesTable).values([...input.bundles]); | |
| 81 | − } | |
| 82 | − }), | |
| 83 | − | |
| 84 | − upsert: (bundle: StoredSourceRecipeSchemaBundleRecord) => | |
| 85 | − client.use("rows.source_recipe_schema_bundles.upsert", async (db) => { | |
| 86 | − await db | |
| 87 | − .insert(tables.sourceRecipeSchemaBundlesTable) | |
| 88 | − .values(bundle) | |
| 89 | − .onConflictDoUpdate({ | |
| 90 | − target: [tables.sourceRecipeSchemaBundlesTable.id], | |
| 91 | − set: { | |
| 92 | − ...withoutCreatedAt(bundle), | |
| 93 | − }, | |
| 94 | − }); | |
| 95 | − }), | |
| 96 | − | |
| 97 | − removeByRevisionId: (recipeRevisionId: StoredSourceRecipeSchemaBundleRecord["recipeRevisionId"]) => | |
| 98 | − client.use("rows.source_recipe_schema_bundles.remove_by_revision", async (db) => { | |
| 99 | − const deleted = await db | |
| 100 | − .delete(tables.sourceRecipeSchemaBundlesTable) | |
| 101 | − .where(eq(tables.sourceRecipeSchemaBundlesTable.recipeRevisionId, recipeRevisionId)) | |
| 102 | − .returning(); | |
| 103 | − | |
| 104 | − return deleted.length; | |
| 105 | − }), | |
| 106 | −}); | |
| @@ -1,67 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecipeRecord, | |
| 3 | − StoredSourceRecipeRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { asc, eq } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption, withoutCreatedAt } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeStoredSourceRecipeRecord = Schema.decodeUnknownSync( | |
| 14 | − StoredSourceRecipeRecordSchema, | |
| 15 | −); | |
| 16 | − | |
| 17 | −export const createSourceRecipesRepo = ( | |
| 18 | − client: DrizzleClient, | |
| 19 | − tables: DrizzleTables, | |
| 20 | −) => ({ | |
| 21 | − listByUpdatedAt: () => | |
| 22 | − client.use("rows.source_recipes.list", async (db) => { | |
| 23 | − const rows = await db | |
| 24 | − .select() | |
| 25 | − .from(tables.sourceRecipesTable) | |
| 26 | − .orderBy(asc(tables.sourceRecipesTable.updatedAt), asc(tables.sourceRecipesTable.id)); | |
| 27 | − | |
| 28 | − return rows.map((row) => decodeStoredSourceRecipeRecord(row)); | |
| 29 | − }), | |
| 30 | − | |
| 31 | − getById: (id: StoredSourceRecipeRecord["id"]) => | |
| 32 | − client.use("rows.source_recipes.get_by_id", async (db) => { | |
| 33 | − const rows = await db | |
| 34 | − .select() | |
| 35 | − .from(tables.sourceRecipesTable) | |
| 36 | − .where(eq(tables.sourceRecipesTable.id, id)) | |
| 37 | − .limit(1); | |
| 38 | − | |
| 39 | − const row = firstOption(rows); | |
| 40 | − return Option.isSome(row) | |
| 41 | − ? Option.some(decodeStoredSourceRecipeRecord(row.value)) | |
| 42 | − : Option.none<StoredSourceRecipeRecord>(); | |
| 43 | − }), | |
| 44 | − | |
| 45 | − upsert: (recipe: StoredSourceRecipeRecord) => | |
| 46 | − client.use("rows.source_recipes.upsert", async (db) => { | |
| 47 | − await db | |
| 48 | − .insert(tables.sourceRecipesTable) | |
| 49 | − .values(recipe) | |
| 50 | − .onConflictDoUpdate({ | |
| 51 | − target: [tables.sourceRecipesTable.id], | |
| 52 | − set: { | |
| 53 | − ...withoutCreatedAt(recipe), | |
| 54 | − }, | |
| 55 | − }); | |
| 56 | − }), | |
| 57 | − | |
| 58 | − removeById: (id: StoredSourceRecipeRecord["id"]) => | |
| 59 | − client.use("rows.source_recipes.remove", async (db) => { | |
| 60 | − const deleted = await db | |
| 61 | − .delete(tables.sourceRecipesTable) | |
| 62 | − .where(eq(tables.sourceRecipesTable.id, id)) | |
| 63 | − .returning(); | |
| 64 | − | |
| 65 | − return deleted.length > 0; | |
| 66 | − }), | |
| 67 | −}); | |
| @@ -1,154 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − type StoredSourceRecord, | |
| 3 | − StoredSourceRecordSchema, | |
| 4 | −} from "#schema"; | |
| 5 | −import * as Option from "effect/Option"; | |
| 6 | −import { Schema } from "effect"; | |
| 7 | −import { and, asc, count, eq } from "drizzle-orm"; | |
| 8 | − | |
| 9 | −import type { DrizzleClient } from "../client"; | |
| 10 | −import type { DrizzleTables } from "../schema"; | |
| 11 | −import { firstOption } from "./shared"; | |
| 12 | − | |
| 13 | −const decodeStoredSourceRecord = Schema.decodeUnknownSync(StoredSourceRecordSchema); | |
| 14 | −const encodeStoredSourceRecord = Schema.encodeSync(StoredSourceRecordSchema); | |
| 15 | − | |
| 16 | −const toSourceUpdateSet = ( | |
| 17 | − patch: Partial<Omit<StoredSourceRecord, "id" | "workspaceId" | "createdAt">>, | |
| 18 | −): Partial<DrizzleTables["sourcesTable"]["$inferInsert"]> => { | |
| 19 | − const set: Partial<DrizzleTables["sourcesTable"]["$inferInsert"]> = {}; | |
| 20 | − | |
| 21 | − if (patch.recipeId !== undefined) set.recipeId = patch.recipeId; | |
| 22 | − if (patch.recipeRevisionId !== undefined) set.recipeRevisionId = patch.recipeRevisionId; | |
| 23 | − if (patch.name !== undefined) set.name = patch.name; | |
| 24 | − if (patch.kind !== undefined) set.kind = patch.kind; | |
| 25 | − if (patch.endpoint !== undefined) set.endpoint = patch.endpoint; | |
| 26 | − if (patch.status !== undefined) set.status = patch.status; | |
| 27 | − if (patch.enabled !== undefined) set.enabled = patch.enabled; | |
| 28 | − if (patch.namespace !== undefined) set.namespace = patch.namespace; | |
| 29 | − if (patch.importAuthPolicy !== undefined) set.importAuthPolicy = patch.importAuthPolicy; | |
| 30 | − if (patch.bindingConfigJson !== undefined) set.bindingConfigJson = patch.bindingConfigJson; | |
| 31 | − if (patch.sourceHash !== undefined) set.sourceHash = patch.sourceHash; | |
| 32 | − if (patch.lastError !== undefined) set.lastError = patch.lastError; | |
| 33 | − if (patch.updatedAt !== undefined) set.updatedAt = patch.updatedAt; | |
| 34 | − | |
| 35 | − return set; | |
| 36 | −}; | |
| 37 | − | |
| 38 | −export const createSourcesRepo = ( | |
| 39 | − client: DrizzleClient, | |
| 40 | − tables: DrizzleTables, | |
| 41 | −) => ({ | |
| 42 | − listAll: () => | |
| 43 | − client.use("rows.sources.list_all", async (db) => { | |
| 44 | − const rows = await db | |
| 45 | − .select() | |
| 46 | − .from(tables.sourcesTable) | |
| 47 | − .orderBy( | |
| 48 | − asc(tables.sourcesTable.workspaceId), | |
| 49 | − asc(tables.sourcesTable.updatedAt), | |
| 50 | − asc(tables.sourcesTable.sourceId), | |
| 51 | − ); | |
| 52 | − | |
| 53 | − return rows.map((row) => decodeStoredSourceRecord(row)); | |
| 54 | − }), | |
| 55 | − | |
| 56 | − listByWorkspaceId: (workspaceId: StoredSourceRecord["workspaceId"]) => | |
| 57 | − client.use("rows.sources.list_by_workspace", async (db) => { | |
| 58 | − const rows = await db | |
| 59 | − .select() | |
| 60 | − .from(tables.sourcesTable) | |
| 61 | − .where(eq(tables.sourcesTable.workspaceId, workspaceId)) | |
| 62 | − .orderBy(asc(tables.sourcesTable.updatedAt), asc(tables.sourcesTable.sourceId)); | |
| 63 | − | |
| 64 | − return rows.map((row) => decodeStoredSourceRecord(row)); | |
| 65 | − }), | |
| 66 | − | |
| 67 | − getByWorkspaceAndId: ( | |
| 68 | − workspaceId: StoredSourceRecord["workspaceId"], | |
| 69 | − sourceId: StoredSourceRecord["id"], | |
| 70 | − ) => | |
| 71 | − client.use("rows.sources.get_by_workspace_and_id", async (db) => { | |
| 72 | − const rows = await db | |
| 73 | − .select() | |
| 74 | − .from(tables.sourcesTable) | |
| 75 | − .where( | |
| 76 | − and( | |
| 77 | − eq(tables.sourcesTable.workspaceId, workspaceId), | |
| 78 | − eq(tables.sourcesTable.sourceId, sourceId), | |
| 79 | − ), | |
| 80 | − ) | |
| 81 | − .limit(1); | |
| 82 | − | |
| 83 | − const row = firstOption(rows); | |
| 84 | − return Option.isSome(row) | |
| 85 | − ? Option.some(decodeStoredSourceRecord(row.value)) | |
| 86 | − : Option.none<StoredSourceRecord>(); | |
| 87 | − }), | |
| 88 | − | |
| 89 | − countByRecipeId: (recipeId: StoredSourceRecord["recipeId"]) => | |
| 90 | − client.use("rows.sources.count_by_recipe", async (db) => { | |
| 91 | − const rows = await db | |
| 92 | − .select({ value: count() }) | |
| 93 | − .from(tables.sourcesTable) | |
| 94 | − .where(eq(tables.sourcesTable.recipeId, recipeId)); | |
| 95 | − | |
| 96 | − return Number(rows[0]?.value ?? 0); | |
| 97 | − }), | |
| 98 | − | |
| 99 | − countByRecipeRevisionId: (recipeRevisionId: StoredSourceRecord["recipeRevisionId"]) => | |
| 100 | − client.use("rows.sources.count_by_recipe_revision", async (db) => { | |
| 101 | − const rows = await db | |
| 102 | − .select({ value: count() }) | |
| 103 | − .from(tables.sourcesTable) | |
| 104 | − .where(eq(tables.sourcesTable.recipeRevisionId, recipeRevisionId)); | |
| 105 | − | |
| 106 | − return Number(rows[0]?.value ?? 0); | |
| 107 | − }), | |
| 108 | − | |
| 109 | − insert: (source: StoredSourceRecord) => | |
| 110 | − client.use("rows.sources.insert", async (db) => { | |
| 111 | − await db.insert(tables.sourcesTable).values(encodeStoredSourceRecord(source)); | |
| 112 | − }), | |
| 113 | − | |
| 114 | − update: ( | |
| 115 | − workspaceId: StoredSourceRecord["workspaceId"], | |
| 116 | − sourceId: StoredSourceRecord["id"], | |
| 117 | − patch: Partial<Omit<StoredSourceRecord, "id" | "workspaceId" | "createdAt">>, | |
| 118 | − ) => | |
| 119 | − client.use("rows.sources.update", async (db) => { | |
| 120 | − const rows = await db | |
| 121 | − .update(tables.sourcesTable) | |
| 122 | − .set(toSourceUpdateSet(patch)) | |
| 123 | − .where( | |
| 124 | − and( | |
| 125 | − eq(tables.sourcesTable.workspaceId, workspaceId), | |
| 126 | − eq(tables.sourcesTable.sourceId, sourceId), | |
| 127 | − ), | |
| 128 | − ) | |
| 129 | − .returning(); | |
| 130 | − | |
| 131 | − const row = firstOption(rows); | |
| 132 | − return Option.isSome(row) | |
| 133 | − ? Option.some(decodeStoredSourceRecord(row.value)) | |
| 134 | − : Option.none<StoredSourceRecord>(); | |
| 135 | − }), | |
| 136 | − | |
| 137 | − removeByWorkspaceAndId: ( | |
| 138 | − workspaceId: StoredSourceRecord["workspaceId"], | |
| 139 | − sourceId: StoredSourceRecord["id"], | |
| 140 | − ) => | |
| 141 | − client.useTx("rows.sources.remove", async (tx) => { | |
| 142 | − const deleted = await tx | |
| 143 | − .delete(tables.sourcesTable) | |
| 144 | − .where( | |
| 145 | − and( | |
| 146 | − eq(tables.sourcesTable.workspaceId, workspaceId), | |
| 147 | − eq(tables.sourcesTable.sourceId, sourceId), | |
| 148 | − ), | |
| 149 | − ) | |
| 150 | − .returning(); | |
| 151 | − | |
| 152 | − return deleted.length > 0; | |
| 153 | − }), | |
| 154 | −}); | |
| @@ -1,550 +0,0 @@ | ||
| 1 | −import { | |
| 2 | − bigint, | |
| 3 | − boolean, | |
| 4 | − check, | |
| 5 | − index, | |
| 6 | − pgTable, | |
| 7 | − primaryKey, | |
| 8 | − text, | |
| 9 | − uniqueIndex, | |
| 10 | −} from "drizzle-orm/pg-core"; | |
| 11 | −import { sql } from "drizzle-orm"; | |
| 12 | − | |
| 13 | −export const tableNames = { | |
| 14 | − accounts: "accounts", | |
| 15 | − sources: "sources", | |
| 16 | − sourceRecipes: "source_recipes", | |
| 17 | − sourceRecipeRevisions: "source_recipe_revisions", | |
| 18 | − sourceRecipeDocuments: "source_recipe_documents", | |
| 19 | − sourceRecipeSchemaBundles: "source_recipe_schema_bundles", | |
| 20 | − sourceRecipeOperations: "source_recipe_operations", | |
| 21 | − codeMigrations: "control_plane_code_migrations", | |
| 22 | − authArtifacts: "workspace_source_auth_artifacts", | |
| 23 | − authLeases: "workspace_source_auth_leases", | |
| 24 | − workspaceSourceOauthClients: "workspace_source_oauth_clients", | |
| 25 | − secretMaterials: "secret_materials", | |
| 26 | − sourceAuthSessions: "source_auth_sessions", | |
| 27 | − executions: "executions", | |
| 28 | − executionInteractions: "execution_interactions", | |
| 29 | − executionSteps: "execution_steps", | |
| 30 | −} as const; | |
| 31 | − | |
| 32 | −export const accountsTable = pgTable( | |
| 33 | − tableNames.accounts, | |
| 34 | − { | |
| 35 | − id: text("id").notNull().primaryKey(), | |
| 36 | − provider: text("provider").notNull(), | |
| 37 | − subject: text("subject").notNull(), | |
| 38 | − email: text("email"), | |
| 39 | − displayName: text("display_name"), | |
| 40 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 41 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 42 | − }, | |
| 43 | − (table) => [ | |
| 44 | − uniqueIndex("accounts_provider_subject_idx").on(table.provider, table.subject), | |
| 45 | − index("accounts_updated_idx").on(table.updatedAt, table.id), | |
| 46 | − check( | |
| 47 | − "accounts_provider_check", | |
| 48 | − sql`${table.provider} in ('local', 'workos', 'service')`, | |
| 49 | − ), | |
| 50 | − ], | |
| 51 | −); | |
| 52 | − | |
| 53 | −export const sourcesTable = pgTable( | |
| 54 | − tableNames.sources, | |
| 55 | − { | |
| 56 | − workspaceId: text("workspace_id").notNull(), | |
| 57 | − sourceId: text("source_id").notNull(), | |
| 58 | − recipeId: text("recipe_id").notNull(), | |
| 59 | − recipeRevisionId: text("recipe_revision_id").notNull(), | |
| 60 | − name: text("name").notNull(), | |
| 61 | − kind: text("kind").notNull(), | |
| 62 | − endpoint: text("endpoint").notNull(), | |
| 63 | − status: text("status").notNull(), | |
| 64 | − enabled: boolean("enabled").notNull(), | |
| 65 | − namespace: text("namespace"), | |
| 66 | − importAuthPolicy: text("import_auth_policy").notNull(), | |
| 67 | − bindingConfigJson: text("binding_config_json").notNull(), | |
| 68 | − sourceHash: text("source_hash"), | |
| 69 | − lastError: text("last_error"), | |
| 70 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 71 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 72 | − }, | |
| 73 | − (table) => [ | |
| 74 | − primaryKey({ | |
| 75 | − columns: [table.workspaceId, table.sourceId], | |
| 76 | − }), | |
| 77 | − index("sources_workspace_updated_idx").on( | |
| 78 | − table.workspaceId, | |
| 79 | − table.updatedAt, | |
| 80 | − table.sourceId, | |
| 81 | − ), | |
| 82 | − index("sources_workspace_recipe_idx").on( | |
| 83 | − table.workspaceId, | |
| 84 | − table.recipeId, | |
| 85 | − table.updatedAt, | |
| 86 | − table.sourceId, | |
| 87 | − ), | |
| 88 | − uniqueIndex("sources_workspace_name_idx").on(table.workspaceId, table.name), | |
| 89 | − check( | |
| 90 | − "sources_status_check", | |
| 91 | − sql`${table.status} in ('draft', 'probing', 'auth_required', 'connected', 'error')`, | |
| 92 | − ), | |
| 93 | − check( | |
| 94 | − "sources_import_auth_policy_check", | |
| 95 | − sql`${table.importAuthPolicy} in ('none', 'reuse_runtime', 'separate')`, | |
| 96 | − ), | |
| 97 | − ], | |
| 98 | −); | |
| 99 | − | |
| 100 | −export const sourceRecipesTable = pgTable( | |
| 101 | − tableNames.sourceRecipes, | |
| 102 | − { | |
| 103 | − id: text("id").notNull().primaryKey(), | |
| 104 | − kind: text("kind").notNull(), | |
| 105 | − adapterKey: text("adapter_key").notNull(), | |
| 106 | − providerKey: text("provider_key").notNull(), | |
| 107 | − name: text("name").notNull(), | |
| 108 | − summary: text("summary"), | |
| 109 | − visibility: text("visibility").notNull(), | |
| 110 | − latestRevisionId: text("latest_revision_id").notNull(), | |
| 111 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 112 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 113 | − }, | |
| 114 | − (table) => [ | |
| 115 | − index("source_recipes_provider_updated_idx").on( | |
| 116 | − table.providerKey, | |
| 117 | − table.updatedAt, | |
| 118 | − table.id, | |
| 119 | − ), | |
| 120 | − index("source_recipes_visibility_updated_idx").on( | |
| 121 | − table.visibility, | |
| 122 | − table.updatedAt, | |
| 123 | − table.id, | |
| 124 | − ), | |
| 125 | − check( | |
| 126 | − "source_recipes_kind_check", | |
| 127 | − sql`${table.kind} in ('http_api', 'mcp', 'internal')`, | |
| 128 | − ), | |
| 129 | − check( | |
| 130 | − "source_recipes_visibility_check", | |
| 131 | − sql`${table.visibility} in ('private', 'workspace', 'organization', 'public')`, | |
| 132 | − ), | |
| 133 | − ], | |
| 134 | −); | |
| 135 | − | |
| 136 | −export const sourceRecipeRevisionsTable = pgTable( | |
| 137 | − tableNames.sourceRecipeRevisions, | |
| 138 | − { | |
| 139 | − id: text("id").notNull().primaryKey(), | |
| 140 | − recipeId: text("recipe_id").notNull(), | |
| 141 | − revisionNumber: bigint("revision_number", { mode: "number" }).notNull(), | |
| 142 | − sourceConfigJson: text("source_config_json").notNull(), | |
| 143 | − manifestJson: text("manifest_json"), | |
| 144 | − manifestHash: text("manifest_hash"), | |
| 145 | − materializationHash: text("materialization_hash"), | |
| 146 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 147 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 148 | − }, | |
| 149 | − (table) => [ | |
| 150 | − uniqueIndex("source_recipe_revisions_recipe_revision_idx").on( | |
| 151 | − table.recipeId, | |
| 152 | − table.revisionNumber, | |
| 153 | − ), | |
| 154 | − uniqueIndex("source_recipe_revisions_recipe_materialization_idx").on( | |
| 155 | − table.recipeId, | |
| 156 | − table.materializationHash, | |
| 157 | − ), | |
| 158 | − index("source_recipe_revisions_recipe_manifest_idx").on( | |
| 159 | − table.recipeId, | |
| 160 | − table.manifestHash, | |
| 161 | − ), | |
| 162 | − index("source_recipe_revisions_recipe_created_idx").on( | |
| 163 | − table.recipeId, | |
| 164 | − table.createdAt, | |
| 165 | − table.id, | |
| 166 | − ), | |
| 167 | − ], | |
| 168 | −); | |
| 169 | − | |
| 170 | −export const sourceRecipeDocumentsTable = pgTable( | |
| 171 | − tableNames.sourceRecipeDocuments, | |
| 172 | − { | |
| 173 | − id: text("id").notNull().primaryKey(), | |
| 174 | − recipeRevisionId: text("recipe_revision_id").notNull(), | |
| 175 | − documentKind: text("document_kind").notNull(), | |
| 176 | − documentKey: text("document_key").notNull(), | |
| 177 | − contentText: text("content_text").notNull(), | |
| 178 | − contentHash: text("content_hash").notNull(), | |
| 179 | − fetchedAt: bigint("fetched_at", { mode: "number" }), | |
| 180 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 181 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 182 | − }, | |
| 183 | − (table) => [ | |
| 184 | − uniqueIndex("source_recipe_documents_revision_kind_key_idx").on( | |
| 185 | − table.recipeRevisionId, | |
| 186 | − table.documentKind, | |
| 187 | − table.documentKey, | |
| 188 | − ), | |
| 189 | − index("source_recipe_documents_revision_created_idx").on( | |
| 190 | − table.recipeRevisionId, | |
| 191 | − table.createdAt, | |
| 192 | − table.id, | |
| 193 | − ), | |
| 194 | − ], | |
| 195 | −); | |
| 196 | − | |
| 197 | −export const sourceRecipeSchemaBundlesTable = pgTable( | |
| 198 | − tableNames.sourceRecipeSchemaBundles, | |
| 199 | − { | |
| 200 | − id: text("id").notNull().primaryKey(), | |
| 201 | − recipeRevisionId: text("recipe_revision_id").notNull(), | |
| 202 | − bundleKind: text("bundle_kind").notNull(), | |
| 203 | − refsJson: text("refs_json").notNull(), | |
| 204 | − contentHash: text("content_hash").notNull(), | |
| 205 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 206 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 207 | − }, | |
| 208 | − (table) => [ | |
| 209 | − uniqueIndex("source_recipe_schema_bundles_revision_kind_idx").on( | |
| 210 | − table.recipeRevisionId, | |
| 211 | − table.bundleKind, | |
| 212 | − ), | |
| 213 | − index("source_recipe_schema_bundles_revision_created_idx").on( | |
| 214 | − table.recipeRevisionId, | |
| 215 | − table.createdAt, | |
| 216 | − table.id, | |
| 217 | − ), | |
| 218 | − ], | |
| 219 | −); | |
| 220 | − | |
| 221 | −export const sourceRecipeOperationsTable = pgTable( | |
| 222 | − tableNames.sourceRecipeOperations, | |
| 223 | − { | |
| 224 | − id: text("id").notNull().primaryKey(), | |
| 225 | − recipeRevisionId: text("recipe_revision_id").notNull(), | |
| 226 | − operationKey: text("operation_key").notNull(), | |
| 227 | − transportKind: text("transport_kind").notNull(), | |
| 228 | − toolId: text("tool_id").notNull(), | |
| 229 | − title: text("title"), | |
| 230 | − description: text("description"), | |
| 231 | − operationKind: text("operation_kind").notNull(), | |
| 232 | − searchText: text("search_text").notNull(), | |
| 233 | − inputSchemaJson: text("input_schema_json"), | |
| 234 | − outputSchemaJson: text("output_schema_json"), | |
| 235 | − providerKind: text("provider_kind").notNull(), | |
| 236 | − providerDataJson: text("provider_data_json"), | |
| 237 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 238 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 239 | − }, | |
| 240 | − (table) => [ | |
| 241 | − uniqueIndex("source_recipe_operations_revision_operation_key_idx").on( | |
| 242 | − table.recipeRevisionId, | |
| 243 | − table.operationKey, | |
| 244 | − ), | |
| 245 | − index("source_recipe_operations_revision_tool_idx").on( | |
| 246 | − table.recipeRevisionId, | |
| 247 | − table.toolId, | |
| 248 | − table.updatedAt, | |
| 249 | − table.id, | |
| 250 | − ), | |
| 251 | − index("source_recipe_operations_search_text_idx").using( | |
| 252 | − "gin", | |
| 253 | − sql`to_tsvector('simple', ${table.searchText})`, | |
| 254 | − ), | |
| 255 | − check( | |
| 256 | − "source_recipe_operations_transport_kind_check", | |
| 257 | − sql`${table.transportKind} in ('http', 'graphql', 'mcp', 'internal')`, | |
| 258 | − ), | |
| 259 | − check( | |
| 260 | − "source_recipe_operations_kind_check", | |
| 261 | − sql`${table.operationKind} in ('read', 'write', 'delete', 'unknown')`, | |
| 262 | − ), | |
| 263 | − ], | |
| 264 | −); | |
| 265 | − | |
| 266 | −export const codeMigrationsTable = pgTable( | |
| 267 | − tableNames.codeMigrations, | |
| 268 | − { | |
| 269 | − id: text("id").notNull().primaryKey(), | |
| 270 | − appliedAt: bigint("applied_at", { mode: "number" }).notNull(), | |
| 271 | − }, | |
| 272 | −); | |
| 273 | − | |
| 274 | −export const authArtifactsTable = pgTable( | |
| 275 | − tableNames.authArtifacts, | |
| 276 | − { | |
| 277 | − id: text("id").notNull().primaryKey(), | |
| 278 | − workspaceId: text("workspace_id").notNull(), | |
| 279 | − sourceId: text("source_id").notNull(), | |
| 280 | − actorAccountId: text("actor_account_id"), | |
| 281 | − slot: text("slot").notNull(), | |
| 282 | − artifactKind: text("artifact_kind").notNull(), | |
| 283 | − configJson: text("config_json").notNull(), | |
| 284 | − grantSetJson: text("grant_set_json"), | |
| 285 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 286 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 287 | − }, | |
| 288 | − (table) => [ | |
| 289 | − index("auth_artifacts_workspace_idx").on( | |
| 290 | − table.workspaceId, | |
| 291 | − table.updatedAt, | |
| 292 | − table.id, | |
| 293 | − ), | |
| 294 | − uniqueIndex("auth_artifacts_workspace_source_actor_idx").on( | |
| 295 | − table.workspaceId, | |
| 296 | − table.sourceId, | |
| 297 | − table.actorAccountId, | |
| 298 | − table.slot, | |
| 299 | − ), | |
| 300 | − index("auth_artifacts_workspace_source_idx").on( | |
| 301 | − table.workspaceId, | |
| 302 | − table.sourceId, | |
| 303 | − table.updatedAt, | |
| 304 | − table.id, | |
| 305 | − ), | |
| 306 | − check( | |
| 307 | − "auth_artifacts_slot_check", | |
| 308 | − sql`${table.slot} in ('runtime', 'import')`, | |
| 309 | − ), | |
| 310 | − ], | |
| 311 | −); | |
| 312 | − | |
| 313 | −export const authLeasesTable = pgTable( | |
| 314 | − tableNames.authLeases, | |
| 315 | − { | |
| 316 | − id: text("id").notNull().primaryKey(), | |
| 317 | − authArtifactId: text("auth_artifact_id").notNull(), | |
| 318 | − workspaceId: text("workspace_id").notNull(), | |
| 319 | − sourceId: text("source_id").notNull(), | |
| 320 | − actorAccountId: text("actor_account_id"), | |
| 321 | − slot: text("slot").notNull(), | |
| 322 | − placementsTemplateJson: text("placements_template_json").notNull(), | |
| 323 | − expiresAt: bigint("expires_at", { mode: "number" }), | |
| 324 | − refreshAfter: bigint("refresh_after", { mode: "number" }), | |
| 325 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 326 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 327 | − }, | |
| 328 | − (table) => [ | |
| 329 | − uniqueIndex("auth_leases_auth_artifact_idx").on(table.authArtifactId), | |
| 330 | − index("auth_leases_workspace_source_idx").on( | |
| 331 | − table.workspaceId, | |
| 332 | − table.sourceId, | |
| 333 | − table.actorAccountId, | |
| 334 | − table.slot, | |
| 335 | − table.updatedAt, | |
| 336 | − table.id, | |
| 337 | − ), | |
| 338 | − check( | |
| 339 | − "auth_leases_slot_check", | |
| 340 | − sql`${table.slot} in ('runtime', 'import')`, | |
| 341 | − ), | |
| 342 | − ], | |
| 343 | −); | |
| 344 | − | |
| 345 | −export const workspaceSourceOauthClientsTable = pgTable( | |
| 346 | − tableNames.workspaceSourceOauthClients, | |
| 347 | − { | |
| 348 | − id: text("id").notNull().primaryKey(), | |
| 349 | − workspaceId: text("workspace_id").notNull(), | |
| 350 | − sourceId: text("source_id").notNull(), | |
| 351 | − providerKey: text("provider_key").notNull(), | |
| 352 | − clientId: text("client_id").notNull(), | |
| 353 | − clientSecretProviderId: text("client_secret_provider_id"), | |
| 354 | − clientSecretHandle: text("client_secret_handle"), | |
| 355 | − clientMetadataJson: text("client_metadata_json"), | |
| 356 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 357 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 358 | − }, | |
| 359 | − (table) => [ | |
| 360 | − uniqueIndex("workspace_source_oauth_clients_workspace_source_provider_idx").on( | |
| 361 | − table.workspaceId, | |
| 362 | − table.sourceId, | |
| 363 | − table.providerKey, | |
| 364 | − ), | |
| 365 | − index("workspace_source_oauth_clients_workspace_idx").on( | |
| 366 | − table.workspaceId, | |
| 367 | − table.updatedAt, | |
| 368 | − table.sourceId, | |
| 369 | − ), | |
| 370 | − ], | |
| 371 | −); | |
| 372 | − | |
| 373 | −export const secretMaterialsTable = pgTable( | |
| 374 | − tableNames.secretMaterials, | |
| 375 | − { | |
| 376 | − id: text("id").notNull().primaryKey(), | |
| 377 | − name: text("name"), | |
| 378 | − purpose: text("purpose").notNull(), | |
| 379 | − value: text("value").notNull(), | |
| 380 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 381 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 382 | − }, | |
| 383 | − (table) => [ | |
| 384 | − index("secret_materials_updated_idx").on(table.updatedAt, table.id), | |
| 385 | − check( | |
| 386 | − "secret_materials_purpose_check", | |
| 387 | − sql`${table.purpose} in ('auth_material', 'oauth_access_token', 'oauth_refresh_token', 'oauth_client_info')`, | |
| 388 | − ), | |
| 389 | − ], | |
| 390 | −); | |
| 391 | − | |
| 392 | −export const sourceAuthSessionsTable = pgTable( | |
| 393 | − tableNames.sourceAuthSessions, | |
| 394 | − { | |
| 395 | − id: text("id").notNull().primaryKey(), | |
| 396 | − workspaceId: text("workspace_id").notNull(), | |
| 397 | − sourceId: text("source_id").notNull(), | |
| 398 | − actorAccountId: text("actor_account_id"), | |
| 399 | − credentialSlot: text("credential_slot").notNull(), | |
| 400 | − executionId: text("execution_id"), | |
| 401 | − interactionId: text("interaction_id"), | |
| 402 | − providerKind: text("provider_kind").notNull(), | |
| 403 | − status: text("status").notNull(), | |
| 404 | − state: text("state").notNull(), | |
| 405 | − sessionDataJson: text("session_data_json").notNull(), | |
| 406 | − errorText: text("error_text"), | |
| 407 | − completedAt: bigint("completed_at", { mode: "number" }), | |
| 408 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 409 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 410 | − }, | |
| 411 | − (table) => [ | |
| 412 | − index("source_auth_sessions_workspace_idx").on( | |
| 413 | − table.workspaceId, | |
| 414 | − table.updatedAt, | |
| 415 | − table.id, | |
| 416 | − ), | |
| 417 | − index("source_auth_sessions_pending_idx").on( | |
| 418 | − table.workspaceId, | |
| 419 | − table.sourceId, | |
| 420 | − table.actorAccountId, | |
| 421 | − table.credentialSlot, | |
| 422 | − table.status, | |
| 423 | − table.updatedAt, | |
| 424 | − table.id, | |
| 425 | − ), | |
| 426 | − uniqueIndex("source_auth_sessions_state_idx").on(table.state), | |
| 427 | − check( | |
| 428 | − "source_auth_sessions_provider_kind_check", | |
| 429 | − sql`${table.providerKind} in ('mcp_oauth', 'oauth2_pkce')`, | |
| 430 | − ), | |
| 431 | − check( | |
| 432 | − "source_auth_sessions_status_check", | |
| 433 | − sql`${table.status} in ('pending', 'completed', 'failed', 'cancelled')`, | |
| 434 | − ), | |
| 435 | − check( | |
| 436 | − "source_auth_sessions_credential_slot_check", | |
| 437 | − sql`${table.credentialSlot} in ('runtime', 'import')`, | |
| 438 | − ), | |
| 439 | − ], | |
| 440 | −); | |
| 441 | − | |
| 442 | −export const executionsTable = pgTable( | |
| 443 | − tableNames.executions, | |
| 444 | − { | |
| 445 | − id: text("id").notNull().primaryKey(), | |
| 446 | − workspaceId: text("workspace_id").notNull(), | |
| 447 | − createdByAccountId: text("created_by_account_id").notNull(), | |
| 448 | − status: text("status").notNull(), | |
| 449 | − code: text("code").notNull(), | |
| 450 | − resultJson: text("result_json"), | |
| 451 | − errorText: text("error_text"), | |
| 452 | − logsJson: text("logs_json"), | |
| 453 | − startedAt: bigint("started_at", { mode: "number" }), | |
| 454 | − completedAt: bigint("completed_at", { mode: "number" }), | |
| 455 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 456 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 457 | − }, | |
| 458 | − (table) => [ | |
| 459 | − index("executions_workspace_idx").on(table.workspaceId, table.updatedAt, table.id), | |
| 460 | − check( | |
| 461 | − "executions_status_check", | |
| 462 | − sql`${table.status} in ('pending', 'running', 'waiting_for_interaction', 'completed', 'failed', 'cancelled')`, | |
| 463 | − ), | |
| 464 | − ], | |
| 465 | −); | |
| 466 | − | |
| 467 | −export const executionInteractionsTable = pgTable( | |
| 468 | − tableNames.executionInteractions, | |
| 469 | − { | |
| 470 | − id: text("id").notNull().primaryKey(), | |
| 471 | − executionId: text("execution_id").notNull(), | |
| 472 | − status: text("status").notNull(), | |
| 473 | − kind: text("kind").notNull(), | |
| 474 | − purpose: text("purpose").notNull(), | |
| 475 | − payloadJson: text("payload_json").notNull(), | |
| 476 | − responseJson: text("response_json"), | |
| 477 | − responsePrivateJson: text("response_private_json"), | |
| 478 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 479 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 480 | − }, | |
| 481 | − (table) => [ | |
| 482 | − index("execution_interactions_execution_idx").on( | |
| 483 | − table.executionId, | |
| 484 | − table.updatedAt, | |
| 485 | − table.id, | |
| 486 | − ), | |
| 487 | − check( | |
| 488 | − "execution_interactions_status_check", | |
| 489 | − sql`${table.status} in ('pending', 'resolved', 'cancelled')`, | |
| 490 | − ), | |
| 491 | − ], | |
| 492 | −); | |
| 493 | − | |
| 494 | −export const executionStepsTable = pgTable( | |
| 495 | − tableNames.executionSteps, | |
| 496 | − { | |
| 497 | − id: text("id").notNull().primaryKey(), | |
| 498 | − executionId: text("execution_id").notNull(), | |
| 499 | − sequence: bigint("sequence", { mode: "number" }).notNull(), | |
| 500 | − kind: text("kind").notNull(), | |
| 501 | − status: text("status").notNull(), | |
| 502 | − path: text("path").notNull(), | |
| 503 | − argsJson: text("args_json").notNull(), | |
| 504 | − resultJson: text("result_json"), | |
| 505 | − errorText: text("error_text"), | |
| 506 | − interactionId: text("interaction_id"), | |
| 507 | − createdAt: bigint("created_at", { mode: "number" }).notNull(), | |
| 508 | − updatedAt: bigint("updated_at", { mode: "number" }).notNull(), | |
| 509 | − }, | |
| 510 | − (table) => [ | |
| 511 | − uniqueIndex("execution_steps_execution_sequence_idx").on( | |
| 512 | − table.executionId, | |
| 513 | − table.sequence, | |
| 514 | − ), | |
| 515 | − index("execution_steps_execution_updated_idx").on( | |
| 516 | − table.executionId, | |
| 517 | − table.updatedAt, | |
| 518 | − table.id, | |
| 519 | − ), | |
| 520 | − check( | |
| 521 | − "execution_steps_kind_check", | |
| 522 | − sql`${table.kind} in ('tool_call')`, | |
| 523 | − ), | |
| 524 | − check( | |
| 525 | − "execution_steps_status_check", | |
| 526 | − sql`${table.status} in ('pending', 'waiting', 'completed', 'failed')`, | |
| 527 | − ), | |
| 528 | − ], | |
| 529 | −); | |
| 530 | − | |
| 531 | −export const drizzleSchema = { | |
| 532 | − accountsTable, | |
| 533 | − sourcesTable, | |
| 534 | − sourceRecipesTable, | |
| 535 | − sourceRecipeRevisionsTable, | |
| 536 | − sourceRecipeDocumentsTable, | |
| 537 | − sourceRecipeSchemaBundlesTable, | |
| 538 | − sourceRecipeOperationsTable, | |
| 539 | − codeMigrationsTable, | |
| 540 | − authArtifactsTable, | |
| 541 | − authLeasesTable, | |
| 542 | − workspaceSourceOauthClientsTable, | |
| 543 | − secretMaterialsTable, | |
| 544 | − sourceAuthSessionsTable, | |
| 545 | − executionsTable, | |
| 546 | − executionInteractionsTable, | |
| 547 | − executionStepsTable, | |
| 548 | −}; | |
| 549 | − | |
| 550 | −export type DrizzleTables = typeof drizzleSchema; | |
| @@ -1,206 +0,0 @@ | ||
| 1 | −import { PGlite } from "@electric-sql/pglite"; | |
| 2 | −import { drizzle as drizzlePGlite } from "drizzle-orm/pglite"; | |
| 3 | −import { migrate as migratePGlite } from "drizzle-orm/pglite/migrator"; | |
| 4 | −import { drizzle as drizzlePostgres } from "drizzle-orm/postgres-js"; | |
| 5 | −import { migrate as migratePostgres } from "drizzle-orm/postgres-js/migrator"; | |
| 6 | −import { existsSync, readdirSync, readFileSync } from "node:fs"; | |
| 7 | −import { mkdir, unlink } from "node:fs/promises"; | |
| 8 | −import * as path from "node:path"; | |
| 9 | −import { fileURLToPath } from "node:url"; | |
| 10 | −import postgres from "postgres"; | |
| 11 | − | |
| 12 | −import { drizzleSchema } from "./schema"; | |
| 13 | − | |
| 14 | −export type SqlBackend = "pglite" | "postgres"; | |
| 15 | − | |
| 16 | −export type CreateSqlRuntimeOptions = { | |
| 17 | − databaseUrl?: string; | |
| 18 | − localDataDir?: string; | |
| 19 | − postgresApplicationName?: string; | |
| 20 | − migrationsFolder?: string; | |
| 21 | −}; | |
| 22 | − | |
| 23 | −const createPGliteDb = (client: PGlite) => | |
| 24 | − drizzlePGlite({ client, schema: drizzleSchema }); | |
| 25 | − | |
| 26 | −const createPostgresDb = (client: postgres.Sql) => | |
| 27 | − drizzlePostgres({ client, schema: drizzleSchema }); | |
| 28 | − | |
| 29 | −export type PGliteDb = ReturnType<typeof createPGliteDb>; | |
| 30 | −export type PostgresDb = ReturnType<typeof createPostgresDb>; | |
| 31 | −export type DrizzleDb = PGliteDb | PostgresDb; | |
| 32 | − | |
| 33 | −export type SqlRuntime = { | |
| 34 | − backend: SqlBackend; | |
| 35 | − db: DrizzleDb; | |
| 36 | − close: () => Promise<void>; | |
| 37 | −}; | |
| 38 | − | |
| 39 | −const trim = (value: string | undefined): string | undefined => { | |
| 40 | − const candidate = value?.trim(); | |
| 41 | − return candidate && candidate.length > 0 ? candidate : undefined; | |
| 42 | −}; | |
| 43 | − | |
| 44 | −const isPostgresUrl = (value: string): boolean => | |
| 45 | − value.startsWith("postgres://") || value.startsWith("postgresql://"); | |
| 46 | − | |
| 47 | −/** | |
| 48 | − * Remove a stale PGlite `postmaster.pid` lock file if present. | |
| 49 | − * | |
| 50 | − * PGlite writes this file on open and removes it on close. When a process | |
| 51 | − * exits without calling `PGlite.close()` (e.g. the Vite dev server is | |
| 52 | − * killed with SIGKILL, or the terminal is closed), the lock file is left | |
| 53 | − * behind and prevents subsequent PGlite instances from opening the | |
| 54 | − * database. PGlite uses a synthetic PID (e.g. `-42`) that is never a real | |
| 55 | − * OS process, so we cannot use it to detect a live owner. Instead we rely | |
| 56 | − * on the fact that in the local single-user context only one process | |
| 57 | − * should ever own this database at a time. | |
| 58 | − */ | |
| 59 | −const cleanupStalePGliteLock = async (dataDir: string): Promise<void> => { | |
| 60 | − const lockPath = path.join(dataDir, "postmaster.pid"); | |
| 61 | − if (!existsSync(lockPath)) { | |
| 62 | − return; | |
| 63 | − } | |
| 64 | − | |
| 65 | − // PGlite's synthetic PID is always negative (e.g. -42). A real Postgres | |
| 66 | − // postmaster would write a positive PID. If we ever see a positive PID | |
| 67 | − // we leave the file alone. | |
| 68 | − try { | |
| 69 | − const content = readFileSync(lockPath, "utf-8"); | |
| 70 | − const firstLine = content.split("\n")[0]?.trim(); | |
| 71 | − const pid = Number(firstLine); | |
| 72 | − if (!Number.isNaN(pid) && pid > 0) { | |
| 73 | − // Looks like a real Postgres PID — don't touch it. | |
| 74 | − return; | |
| 75 | − } | |
| 76 | − } catch { | |
| 77 | − // If we can't read the file, try to remove it anyway. | |
| 78 | − } | |
| 79 | − | |
| 80 | − try { | |
| 81 | − await unlink(lockPath); | |
| 82 | − } catch { | |
| 83 | − // Best-effort cleanup; PGlite will report the real error if this fails. | |
| 84 | − } | |
| 85 | −}; | |
| 86 | − | |
| 87 | −const createPGliteRuntime = async (localDataDir: string): Promise<SqlRuntime> => { | |
| 88 | − const normalized = trim(localDataDir) ?? ".executor/control-plane-pgdata"; | |
| 89 | − | |
| 90 | − let client: PGlite; | |
| 91 | − if (normalized === ":memory:") { | |
| 92 | − client = new PGlite(); | |
| 93 | − } else { | |
| 94 | − const resolvedDataDir = path.resolve(normalized); | |
| 95 | − if (!existsSync(resolvedDataDir)) { | |
| 96 | − await mkdir(resolvedDataDir, { recursive: true }); | |
| 97 | − } | |
| 98 | − await cleanupStalePGliteLock(resolvedDataDir); | |
| 99 | − client = new PGlite(resolvedDataDir); | |
| 100 | − } | |
| 101 | − | |
| 102 | − const db = createPGliteDb(client); | |
| 103 | − | |
| 104 | − return { | |
| 105 | − backend: "pglite", | |
| 106 | − db, | |
| 107 | − close: async () => { | |
| 108 | − await client.close(); | |
| 109 | − }, | |
| 110 | − }; | |
| 111 | −}; | |
| 112 | − | |
| 113 | −const createPostgresRuntime = async ( | |
| 114 | − databaseUrl: string, | |
| 115 | − applicationName: string | undefined, | |
| 116 | −): Promise<SqlRuntime> => { | |
| 117 | − const client = postgres(databaseUrl, { | |
| 118 | − prepare: false, | |
| 119 | − max: 10, | |
| 120 | − ...(applicationName | |
| 121 | − ? { connection: { application_name: applicationName } } | |
| 122 | − : {}), | |
| 123 | − }); | |
| 124 | − const db = createPostgresDb(client); | |
| 125 | − | |
| 126 | − return { | |
| 127 | − backend: "postgres", | |
| 128 | − db, | |
| 129 | − close: async () => { | |
| 130 | − await client.end({ timeout: 5 }); | |
| 131 | − }, | |
| 132 | − }; | |
| 133 | −}; | |
| 134 | − | |
| 135 | −const resolveMigrationsFolder = (explicit: string | undefined): string => { | |
| 136 | − const explicitTrimmed = trim(explicit); | |
| 137 | − if (explicitTrimmed) { | |
| 138 | − return path.resolve(explicitTrimmed); | |
| 139 | − } | |
| 140 | − | |
| 141 | − const moduleDir = path.dirname(fileURLToPath(import.meta.url)); | |
| 142 | − const packageRoot = path.resolve(moduleDir, "../.."); | |
| 143 | − const cwd = process.cwd(); | |
| 144 | − const candidates = [ | |
| 145 | − path.resolve(packageRoot, "drizzle"), | |
| 146 | − path.resolve(cwd, "packages/control-plane/drizzle"), | |
| 147 | − path.resolve(cwd, "drizzle"), | |
| 148 | − ]; | |
| 149 | − | |
| 150 | − const hasModernMigrations = (candidate: string): boolean => { | |
| 151 | − if (!existsSync(candidate)) { | |
| 152 | − return false; | |
| 153 | − } | |
| 154 | − | |
| 155 | − const entries = readdirSync(candidate, { withFileTypes: true }); | |
| 156 | − return entries.some( | |
| 157 | − (entry) => | |
| 158 | − entry.isDirectory() | |
| 159 | − && existsSync(path.join(candidate, entry.name, "migration.sql")), | |
| 160 | − ); | |
| 161 | − }; | |
| 162 | − | |
| 163 | − for (const candidate of candidates) { | |
| 164 | − if ( | |
| 165 | − existsSync(path.join(candidate, "meta", "_journal.json")) | |
| 166 | − || hasModernMigrations(candidate) | |
| 167 | − ) { | |
| 168 | − return candidate; | |
| 169 | − } | |
| 170 | − } | |
| 171 | − | |
| 172 | − throw new Error( | |
| 173 | − "Unable to resolve Drizzle migrations folder for control-plane persistence", | |
| 174 | − ); | |
| 175 | −}; | |
| 176 | − | |
| 177 | −export const runMigrations = async ( | |
| 178 | − runtime: SqlRuntime, | |
| 179 | − options?: { migrationsFolder?: string }, | |
| 180 | −): Promise<void> => { | |
| 181 | − const migrationsFolder = resolveMigrationsFolder(options?.migrationsFolder); | |
| 182 | − | |
| 183 | − if (runtime.backend === "pglite") { | |
| 184 | − await migratePGlite(runtime.db as PGliteDb, { migrationsFolder }); | |
| 185 | − return; | |
| 186 | − } | |
| 187 | − | |
| 188 | − await migratePostgres(runtime.db as PostgresDb, { migrationsFolder }); | |
| 189 | −}; | |
| 190 | − | |
| 191 | −export const createSqlRuntime = async ( | |
| 192 | − options: CreateSqlRuntimeOptions, | |
| 193 | −): Promise<SqlRuntime> => { | |
| 194 | − const databaseUrl = trim(options.databaseUrl); | |
| 195 | − const runtime = | |
| 196 | − databaseUrl && isPostgresUrl(databaseUrl) | |
| 197 | − ? await createPostgresRuntime( | |
| 198 | − databaseUrl, | |
| 199 | − trim(options.postgresApplicationName), | |
| 200 | − ) | |
| 201 | − : await createPGliteRuntime( | |
| 202 | − options.localDataDir ?? ".executor/control-plane-pgdata", | |
| 203 | − ); | |
| 204 | − | |
| 205 | − return runtime; | |
| 206 | −}; | |
| @@ -1,4 +1,3 @@ | ||
| 1 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 2 | 1 | import type { |
| 3 | 2 | AuthArtifact, |
| 4 | 3 | AuthLease, |
| @@ -14,6 +13,7 @@ import { · 9 unmodified lines | ||
| 14 | 13 | import * as Effect from "effect/Effect"; |
| 15 | 14 | import * as Option from "effect/Option"; |
| 16 | 15 | |
| 16 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 17 | 17 | import { resolveAuthArtifactMaterial, type ResolvedSourceAuthMaterial } from "./auth-artifacts"; |
| 18 | 18 | import { |
| 19 | 19 | createDefaultSecretMaterialDeleter, |
| @@ -33,7 +33,7 @@ const secretRefKey = (ref: SecretRef): string => · 13 unmodified lines | ||
| 33 | 33 | `${ref.providerId}:${ref.handle}`; |
| 34 | 34 | |
| 35 | 35 | const cleanupAuthLeaseSecretRefs = ( |
| 36 | − rows: SqlControlPlaneRows, | |
| 36 | + rows: ControlPlaneStoreShape, | |
| 37 | 37 | input: { |
| 38 | 38 | previous: Pick<AuthLease, "placementsTemplateJson"> | null; |
| 39 | 39 | next: Pick<AuthLease, "placementsTemplateJson"> | null; |
| @@ -76,7 +76,7 @@ const leaseIsFresh = (lease: AuthLease | null, now: number): boolean => { · 36 unmodified lines | ||
| 76 | 76 | }; |
| 77 | 77 | |
| 78 | 78 | const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { |
| 79 | − rows: SqlControlPlaneRows; | |
| 79 | + rows: ControlPlaneStoreShape; | |
| 80 | 80 | artifact: AuthArtifact; |
| 81 | 81 | lease: AuthLease | null; |
| 82 | 82 | resolveSecretMaterial: ResolveSecretMaterial; |
| @@ -168,7 +168,7 @@ const refreshRefreshableOauth2AuthorizedUserArtifact = (input: { · 85 unmodified lines | ||
| 168 | 168 | return nextLease; |
| 169 | 169 | }); |
| 170 | 170 | |
| 171 | −export const removeAuthLeaseAndSecrets = (rows: SqlControlPlaneRows, input: { | |
| 171 | +export const removeAuthLeaseAndSecrets = (rows: ControlPlaneStoreShape, input: { | |
| 172 | 172 | authArtifactId: AuthArtifact["id"]; |
| 173 | 173 | }): Effect.Effect<void, Error, never> => |
| 174 | 174 | Effect.gen(function* () { |
| @@ -185,7 +185,7 @@ export const removeAuthLeaseAndSecrets = (rows: SqlControlPlaneRows, input: { · 10 unmodified lines | ||
| 185 | 185 | }); |
| 186 | 186 | |
| 187 | 187 | export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { |
| 188 | − rows: SqlControlPlaneRows; | |
| 188 | + rows: ControlPlaneStoreShape; | |
| 189 | 189 | artifact: AuthArtifact; |
| 190 | 190 | tokenResponse: OAuth2TokenResponse; |
| 191 | 191 | }): Effect.Effect<void, Error, never> => |
| @@ -256,7 +256,7 @@ export const upsertOauth2AuthorizedUserLeaseFromTokenResponse = (input: { · 64 unmodified lines | ||
| 256 | 256 | }); |
| 257 | 257 | |
| 258 | 258 | export const resolveAuthArtifactMaterialWithLeases = (input: { |
| 259 | − rows: SqlControlPlaneRows; | |
| 259 | + rows: ControlPlaneStoreShape; | |
| 260 | 260 | artifact: AuthArtifact | null; |
| 261 | 261 | resolveSecretMaterial: ResolveSecretMaterial; |
| 262 | 262 | context?: SecretMaterialResolveContext; |
| @@ -319,7 +319,7 @@ describe("control-plane-runtime", () => { · 318 unmodified lines | ||
| 319 | 319 | decodedCredentialSelection.authKind === "bearer" |
| 320 | 320 | ? decodedCredentialSelection.tokenRef.providerId |
| 321 | 321 | : null, |
| 322 | − ).toBe("postgres"); | |
| 322 | + ).toBe("local"); | |
| 323 | 323 | |
| 324 | 324 | const tokenSecretMaterialId = SecretMaterialIdSchema.make( |
| 325 | 325 | decodedCredentialSelection.authKind === "bearer" |
| @@ -1,271 +0,0 @@ | ||
| 1 | −import { describe, expect, it } from "@effect/vitest"; | |
| 2 | −import * as Effect from "effect/Effect"; | |
| 3 | −import * as Schema from "effect/Schema"; | |
| 4 | − | |
| 5 | −import { makeToolInvokerFromTools } from "@executor/codemode-core"; | |
| 6 | −import { makeDenoSubprocessExecutor } from "@executor/runtime-deno-subprocess"; | |
| 7 | − | |
| 8 | −import { | |
| 9 | − createSqlControlPlaneRuntime, | |
| 10 | − type ResolveExecutionEnvironment, | |
| 11 | −} from "./index"; | |
| 12 | −import { withControlPlaneClient } from "./test-http-client"; | |
| 13 | − | |
| 14 | −const gatedEchoElicitation = { | |
| 15 | − message: "Approve gated echo?", | |
| 16 | − requestedSchema: { | |
| 17 | − type: "object", | |
| 18 | − properties: { | |
| 19 | − approve: { | |
| 20 | − type: "boolean", | |
| 21 | − title: "Approve", | |
| 22 | − }, | |
| 23 | − }, | |
| 24 | − required: ["approve"], | |
| 25 | − }, | |
| 26 | −} as const; | |
| 27 | − | |
| 28 | −const makeReplayResolver = (countedCalls: { value: number }): ResolveExecutionEnvironment => | |
| 29 | − ({ onElicitation }) => | |
| 30 | − Effect.succeed({ | |
| 31 | − executor: makeDenoSubprocessExecutor(), | |
| 32 | − toolInvoker: makeToolInvokerFromTools({ | |
| 33 | − onElicitation, | |
| 34 | − tools: { | |
| 35 | − "counted.add": { | |
| 36 | − description: "Adds numbers and counts calls", | |
| 37 | − inputSchema: Schema.standardSchemaV1( | |
| 38 | − Schema.Struct({ | |
| 39 | − a: Schema.optional(Schema.Number), | |
| 40 | − b: Schema.optional(Schema.Number), | |
| 41 | − }), | |
| 42 | − ), | |
| 43 | − execute: ({ a, b }: { a?: number; b?: number }) => { | |
| 44 | − countedCalls.value += 1; | |
| 45 | − return { sum: (a ?? 0) + (b ?? 0) }; | |
| 46 | − }, | |
| 47 | − }, | |
| 48 | − "gated.echo": { | |
| 49 | − tool: { | |
| 50 | − description: "Echoes only after approval", | |
| 51 | − inputSchema: Schema.standardSchemaV1( | |
| 52 | − Schema.Struct({ | |
| 53 | − value: Schema.String, | |
| 54 | − approve: Schema.optional(Schema.Boolean), | |
| 55 | − }), | |
| 56 | − ), | |
| 57 | − execute: ({ value, approve }: { value: string; approve?: boolean }) => ({ | |
| 58 | − text: approve === true ? `approved:${value}` : `denied:${value}`, | |
| 59 | − }), | |
| 60 | − }, | |
| 61 | − metadata: { | |
| 62 | − interaction: "required", | |
| 63 | − elicitation: gatedEchoElicitation, | |
| 64 | − }, | |
| 65 | − }, | |
| 66 | − }, | |
| 67 | − }), | |
| 68 | − }); | |
| 69 | − | |
| 70 | −const createRuntime = (countedCalls: { value: number }) => | |
| 71 | − Effect.acquireRelease( | |
| 72 | − createSqlControlPlaneRuntime({ | |
| 73 | − localDataDir: ":memory:", | |
| 74 | − executionResolver: makeReplayResolver(countedCalls), | |
| 75 | − }), | |
| 76 | − (runtime) => Effect.promise(() => runtime.close()).pipe(Effect.orDie), | |
| 77 | − ); | |
| 78 | − | |
| 79 | −describe("execution replay", () => { | |
| 80 | − it.scoped("replays completed proxy calls instead of re-executing them", () => | |
| 81 | − Effect.gen(function* () { | |
| 82 | − const countedCalls = { value: 0 }; | |
| 83 | − const runtime = yield* createRuntime(countedCalls); | |
| 84 | − const installation = runtime.localInstallation; | |
| 85 | − | |
| 86 | − const created = yield* withControlPlaneClient( | |
| 87 | − { | |
| 88 | − runtime, | |
| 89 | − accountId: installation.accountId, | |
| 90 | − }, | |
| 91 | − (client) => | |
| 92 | − client.executions.create({ | |
| 93 | − path: { | |
| 94 | − workspaceId: installation.workspaceId, | |
| 95 | − }, | |
| 96 | − payload: { | |
| 97 | − code: [ | |
| 98 | − "const sum = await tools.counted.add({ a: 20, b: 22 });", | |
| 99 | − 'const echo = await tools.gated.echo({ value: "from-replay" });', | |
| 100 | − "return { sum, echo };", | |
| 101 | − ].join("\n"), | |
| 102 | − }, | |
| 103 | − }), | |
| 104 | − ); | |
| 105 | − | |
| 106 | − expect(created.execution.status).toBe("waiting_for_interaction"); | |
| 107 | − expect(countedCalls.value).toBe(1); | |
| 108 | − | |
| 109 | − const resumed = yield* withControlPlaneClient( | |
| 110 | − { | |
| 111 | − runtime, | |
| 112 | − accountId: installation.accountId, | |
| 113 | − }, | |
| 114 | − (client) => | |
| 115 | − client.executions.resume({ | |
| 116 | − path: { | |
| 117 | − workspaceId: installation.workspaceId, | |
| 118 | − executionId: created.execution.id, | |
| 119 | − }, | |
| 120 | − payload: { | |
| 121 | − responseJson: JSON.stringify({ | |
| 122 | − action: "accept", | |
| 123 | − content: { | |
| 124 | − approve: true, | |
| 125 | − }, | |
| 126 | − }), | |
| 127 | − }, | |
| 128 | − }), | |
| 129 | − ); | |
| 130 | − | |
| 131 | − expect(resumed.execution.status).toBe("completed"); | |
| 132 | − expect(countedCalls.value).toBe(1); | |
| 133 | − expect(resumed.execution.resultJson).toBe( | |
| 134 | − JSON.stringify({ | |
| 135 | − sum: { sum: 42 }, | |
| 136 | − echo: { text: "approved:from-replay" }, | |
| 137 | − }), | |
| 138 | − ); | |
| 139 | − | |
| 140 | − const completedSteps = yield* runtime.persistence.rows.executionSteps.listByExecutionId( | |
| 141 | − created.execution.id, | |
| 142 | − ); | |
| 143 | − expect(completedSteps).toHaveLength(0); | |
| 144 | − }), | |
| 145 | − ); | |
| 146 | − | |
| 147 | − it.scoped("keeps live form interactions in the same run when requested", () => | |
| 148 | − Effect.gen(function* () { | |
| 149 | − const countedCalls = { value: 0 }; | |
| 150 | − const runtime = yield* createRuntime(countedCalls); | |
| 151 | − const installation = runtime.localInstallation; | |
| 152 | − | |
| 153 | − const created = yield* withControlPlaneClient( | |
| 154 | − { | |
| 155 | − runtime, | |
| 156 | − accountId: installation.accountId, | |
| 157 | − }, | |
| 158 | − (client) => | |
| 159 | − client.executions.create({ | |
| 160 | − path: { | |
| 161 | − workspaceId: installation.workspaceId, | |
| 162 | − }, | |
| 163 | − payload: { | |
| 164 | − interactionMode: "live_form", | |
| 165 | − code: [ | |
| 166 | − "const nonce = String(Math.random());", | |
| 167 | − "return await tools.gated.echo({ value: nonce });", | |
| 168 | − ].join("\n"), | |
| 169 | − }, | |
| 170 | − }), | |
| 171 | − ); | |
| 172 | − | |
| 173 | − expect(created.execution.status).toBe("waiting_for_interaction"); | |
| 174 | − | |
| 175 | − const resumed = yield* withControlPlaneClient( | |
| 176 | − { | |
| 177 | − runtime, | |
| 178 | − accountId: installation.accountId, | |
| 179 | − }, | |
| 180 | − (client) => | |
| 181 | − client.executions.resume({ | |
| 182 | − path: { | |
| 183 | − workspaceId: installation.workspaceId, | |
| 184 | − executionId: created.execution.id, | |
| 185 | − }, | |
| 186 | − payload: { | |
| 187 | − interactionMode: "live_form", | |
| 188 | − responseJson: JSON.stringify({ | |
| 189 | − action: "accept", | |
| 190 | − content: { | |
| 191 | − approve: true, | |
| 192 | − }, | |
| 193 | − }), | |
| 194 | − }, | |
| 195 | − }), | |
| 196 | − ); | |
| 197 | − | |
| 198 | − expect(resumed.execution.status).toBe("completed"); | |
| 199 | − expect(resumed.execution.resultJson).toContain("approved:"); | |
| 200 | − }), | |
| 201 | − ); | |
| 202 | − | |
| 203 | − it.scoped("fails loudly when replayed code reaches a different proxy call", () => | |
| 204 | − Effect.gen(function* () { | |
| 205 | − const countedCalls = { value: 0 }; | |
| 206 | − const runtime = yield* createRuntime(countedCalls); | |
| 207 | − const installation = runtime.localInstallation; | |
| 208 | − | |
| 209 | − const created = yield* withControlPlaneClient( | |
| 210 | − { | |
| 211 | − runtime, | |
| 212 | − accountId: installation.accountId, | |
| 213 | − }, | |
| 214 | − (client) => | |
| 215 | − client.executions.create({ | |
| 216 | − path: { | |
| 217 | − workspaceId: installation.workspaceId, | |
| 218 | − }, | |
| 219 | − payload: { | |
| 220 | − code: [ | |
| 221 | − "await tools.counted.add({ a: 1, b: 2 });", | |
| 222 | − 'return await tools.gated.echo({ value: "mismatch" });', | |
| 223 | − ].join("\n"), | |
| 224 | − }, | |
| 225 | − }), | |
| 226 | − ); | |
| 227 | − | |
| 228 | − expect(created.execution.status).toBe("waiting_for_interaction"); | |
| 229 | − expect(countedCalls.value).toBe(1); | |
| 230 | − | |
| 231 | − yield* runtime.persistence.rows.executions.update(created.execution.id, { | |
| 232 | − code: [ | |
| 233 | − "await tools.counted.add({ a: 1, b: 999 });", | |
| 234 | − 'return await tools.gated.echo({ value: "mismatch" });', | |
| 235 | − ].join("\n"), | |
| 236 | − updatedAt: Date.now(), | |
| 237 | − }); | |
| 238 | − | |
| 239 | − const resumed = yield* withControlPlaneClient( | |
| 240 | − { | |
| 241 | − runtime, | |
| 242 | − accountId: installation.accountId, | |
| 243 | − }, | |
| 244 | − (client) => | |
| 245 | − client.executions.resume({ | |
| 246 | − path: { | |
| 247 | − workspaceId: installation.workspaceId, | |
| 248 | − executionId: created.execution.id, | |
| 249 | − }, | |
| 250 | − payload: { | |
| 251 | − responseJson: JSON.stringify({ | |
| 252 | − action: "accept", | |
| 253 | − content: { | |
| 254 | − approve: true, | |
| 255 | − }, | |
| 256 | − }), | |
| 257 | − }, | |
| 258 | − }), | |
| 259 | − ); | |
| 260 | − | |
| 261 | − expect(resumed.execution.status).toBe("failed"); | |
| 262 | − expect(resumed.execution.errorText).toContain("Durable execution mismatch"); | |
| 263 | − expect(countedCalls.value).toBe(1); | |
| 264 | − | |
| 265 | − const failedSteps = yield* runtime.persistence.rows.executionSteps.listByExecutionId( | |
| 266 | − created.execution.id, | |
| 267 | − ); | |
| 268 | − expect(failedSteps).toHaveLength(0); | |
| 269 | − }), | |
| 270 | − ); | |
| 271 | −}); | |
| @@ -18,25 +18,33 @@ import { · 17 unmodified lines | ||
| 18 | 18 | import * as Effect from "effect/Effect"; |
| 19 | 19 | import * as Cause from "effect/Cause"; |
| 20 | 20 | import * as Exit from "effect/Exit"; |
| 21 | +import * as Layer from "effect/Layer"; | |
| 21 | 22 | import * as Schema from "effect/Schema"; |
| 22 | 23 | import type { RuntimeLocalWorkspaceState } from "./local-runtime-context"; |
| 23 | −import { RuntimeLocalWorkspaceService } from "./local-runtime-context"; | |
| 24 | +import { | |
| 25 | + InstallationStore, | |
| 26 | + type LocalStorageServices, | |
| 27 | + LocalInstallationStore, | |
| 28 | + LocalSourceArtifactStore, | |
| 29 | + LocalWorkspaceConfigStore, | |
| 30 | + LocalWorkspaceStateStore, | |
| 31 | + SourceArtifactStore, | |
| 32 | + WorkspaceConfigStore, | |
| 33 | + WorkspaceStateStore, | |
| 34 | +} from "./local-storage"; | |
| 24 | 35 | |
| 25 | 36 | /** Run an Effect as a Promise, preserving the original error (not FiberFailure). */ |
| 26 | 37 | const runEffect = async <A>( |
| 27 | − effect: Effect.Effect<A, unknown, never>, | |
| 28 | − runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null, | |
| 38 | + effect: Effect.Effect<A, unknown, LocalStorageServices>, | |
| 39 | + _runtimeLocalWorkspace: RuntimeLocalWorkspaceState | null = null, | |
| 29 | 40 | ): Promise<A> => { |
| 30 | − const provided = | |
| 31 | − runtimeLocalWorkspace === null | |
| 32 | − ? effect | |
| 33 | − : effect.pipe( | |
| 34 | − Effect.provideService( | |
| 35 | − RuntimeLocalWorkspaceService, | |
| 36 | − runtimeLocalWorkspace, | |
| 37 | − ), | |
| 38 | − ); | |
| 39 | − const exit = await Effect.runPromiseExit(provided); | |
| 41 | + const baseLayer = Layer.mergeAll( | |
| 42 | + Layer.succeed(InstallationStore, LocalInstallationStore), | |
| 43 | + Layer.succeed(WorkspaceConfigStore, LocalWorkspaceConfigStore), | |
| 44 | + Layer.succeed(WorkspaceStateStore, LocalWorkspaceStateStore), | |
| 45 | + Layer.succeed(SourceArtifactStore, LocalSourceArtifactStore), | |
| 46 | + ); | |
| 47 | + const exit = await Effect.runPromiseExit(effect.pipe(Effect.provide(baseLayer))); | |
| 40 | 48 | if (Exit.isSuccess(exit)) return exit.value; |
| 41 | 49 | throw Cause.squash(exit.cause); |
| 42 | 50 | }; |
| @@ -1,19 +1,9 @@ | ||
| 1 | −import * as Context from "effect/Context"; | |
| 2 | 1 | import * as Effect from "effect/Effect"; |
| 3 | 2 | import * as Exit from "effect/Exit"; |
| 4 | 3 | import * as Layer from "effect/Layer"; |
| 5 | 4 | import * as Scope from "effect/Scope"; |
| 6 | 5 | |
| 7 | 6 | import { type ControlPlaneApiRuntimeContext } from "#api"; |
| 8 | −import { | |
| 9 | − SqlControlPlanePersistenceLive, | |
| 10 | − SqlControlPlanePersistenceService, | |
| 11 | − SqlControlPlaneRowsLive, | |
| 12 | − SqlControlPlaneRowsService, | |
| 13 | − SqlPersistenceBootstrapError, | |
| 14 | − type CreateSqlRuntimeOptions, | |
| 15 | − type SqlControlPlanePersistence, | |
| 16 | −} from "#persistence"; | |
| 17 | 7 | import type { LocalInstallation } from "#schema"; |
| 18 | 8 | |
| 19 | 9 | import { type ResolveExecutionEnvironment } from "./execution-state"; |
| @@ -22,14 +12,33 @@ import { · 2 unmodified lines | ||
| 22 | 12 | LiveExecutionManagerLive, |
| 23 | 13 | LiveExecutionManagerService, |
| 24 | 14 | } from "./live-execution"; |
| 25 | −import { getOrProvisionLocalInstallation } from "./local-installation"; | |
| 26 | 15 | import { |
| 27 | − loadLocalExecutorConfig, | |
| 16 | + createLocalControlPlanePersistence, | |
| 17 | + type LocalControlPlanePersistence, | |
| 18 | +} from "./local-control-plane-store"; | |
| 19 | +import { | |
| 28 | 20 | resolveLocalWorkspaceContext, |
| 29 | 21 | } from "./local-config"; |
| 30 | −import { RuntimeLocalWorkspaceService } from "./local-runtime-context"; | |
| 22 | +import { | |
| 23 | + InstallationStore, | |
| 24 | + LocalInstallationStore, | |
| 25 | + LocalInstallationStoreLive, | |
| 26 | + LocalSourceArtifactStore, | |
| 27 | + LocalSourceArtifactStoreLive, | |
| 28 | + SourceArtifactStore, | |
| 29 | + LocalWorkspaceConfigStore, | |
| 30 | + LocalWorkspaceConfigStoreLive, | |
| 31 | + LocalWorkspaceStateStore, | |
| 32 | + LocalWorkspaceStateStoreLive, | |
| 33 | + WorkspaceConfigStore, | |
| 34 | + WorkspaceStateStore, | |
| 35 | +} from "./local-storage"; | |
| 36 | +import { | |
| 37 | + type RuntimeLocalWorkspaceState, | |
| 38 | + RuntimeLocalWorkspaceService, | |
| 39 | +} from "./local-runtime-context"; | |
| 31 | 40 | import { synchronizeLocalWorkspaceState } from "./local-workspace-sync"; |
| 32 | −import { ControlPlaneStore, ControlPlaneStoreLive } from "./store"; | |
| 41 | +import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store"; | |
| 33 | 42 | import { |
| 34 | 43 | createRuntimeSourceAuthService, |
| 35 | 44 | RuntimeSourceAuthServiceLive, |
| @@ -48,6 +57,7 @@ export * from "./executor-tools"; · 12 unmodified lines | ||
| 48 | 57 | export * from "./live-execution"; |
| 49 | 58 | export * from "./local-config"; |
| 50 | 59 | export * from "./local-installation"; |
| 60 | +export * from "./local-storage"; | |
| 51 | 61 | export * from "./local-source-artifacts"; |
| 52 | 62 | export * from "./local-tools"; |
| 53 | 63 | export * from "./schema-type-signature"; |
| @@ -73,12 +83,9 @@ const detailsFromCause = (cause: unknown): string => · 19 unmodified lines | ||
| 73 | 83 | |
| 74 | 84 | const toLocalRuntimeBootstrapError = ( |
| 75 | 85 | cause: unknown, |
| 76 | −): SqlPersistenceBootstrapError => { | |
| 86 | +): Error => { | |
| 77 | 87 | const details = detailsFromCause(cause); |
| 78 | − return new SqlPersistenceBootstrapError({ | |
| 79 | − message: `Failed initializing local runtime: ${details}`, | |
| 80 | − details, | |
| 81 | − }); | |
| 88 | + return new Error(`Failed initializing local runtime: ${details}`); | |
| 82 | 89 | }; |
| 83 | 90 | |
| 84 | 91 | const closeScope = (scope: Scope.CloseableScope) => |
| @@ -90,88 +97,57 @@ export type RuntimeControlPlaneLayer = Layer.Layer< · 5 unmodified lines | ||
| 90 | 97 | never |
| 91 | 98 | >; |
| 92 | 99 | |
| 93 | −const runtimeContextTags = [ | |
| 94 | − ControlPlaneStore, | |
| 95 | − LiveExecutionManagerService, | |
| 96 | − RuntimeLocalWorkspaceService, | |
| 97 | − RuntimeSourceAuthServiceTag, | |
| 98 | − RuntimeExecutionResolverService, | |
| 99 | −] as const; | |
| 100 | − | |
| 101 | −const createRuntimeLayerFromContext = ( | |
| 102 | − context: Context.Context< | |
| 103 | − | ControlPlaneStore | |
| 104 | − | LiveExecutionManagerService | |
| 105 | − | RuntimeLocalWorkspaceService | |
| 106 | − | RuntimeSourceAuthServiceTag | |
| 107 | − | RuntimeExecutionResolverService | |
| 108 | − >, | |
| 109 | −): RuntimeControlPlaneLayer => { | |
| 110 | − const runtimeContext = context.pipe( | |
| 111 | − Context.pick(...runtimeContextTags), | |
| 112 | − ) as Context.Context< | |
| 113 | − | ControlPlaneStore | |
| 114 | − | LiveExecutionManagerService | |
| 115 | − | RuntimeLocalWorkspaceService | |
| 116 | − | RuntimeSourceAuthServiceTag | |
| 117 | − | RuntimeExecutionResolverService | |
| 118 | − >; | |
| 119 | − | |
| 120 | − return Layer.succeedContext(runtimeContext) as RuntimeControlPlaneLayer; | |
| 121 | −}; | |
| 122 | − | |
| 123 | 100 | export const createRuntimeControlPlaneLayer = ( |
| 124 | − options: RuntimeControlPlaneOptions = {}, | |
| 101 | + input: RuntimeControlPlaneOptions & { | |
| 102 | + store: ControlPlaneStoreShape; | |
| 103 | + localWorkspaceState: RuntimeLocalWorkspaceState; | |
| 104 | + }, | |
| 125 | 105 | ) => { |
| 126 | − const liveExecutionManagerLayer = LiveExecutionManagerLive; | |
| 106 | + const baseLayer = Layer.mergeAll( | |
| 107 | + Layer.succeed(ControlPlaneStore, input.store), | |
| 108 | + Layer.succeed(RuntimeLocalWorkspaceService, input.localWorkspaceState), | |
| 109 | + LocalInstallationStoreLive, | |
| 110 | + LocalWorkspaceConfigStoreLive, | |
| 111 | + LocalWorkspaceStateStoreLive, | |
| 112 | + LocalSourceArtifactStoreLive, | |
| 113 | + LiveExecutionManagerLive, | |
| 114 | + ); | |
| 115 | + | |
| 127 | 116 | const sourceAuthLayer = RuntimeSourceAuthServiceLive({ |
| 128 | − getLocalServerBaseUrl: options.getLocalServerBaseUrl, | |
| 129 | − }).pipe(Layer.provide(liveExecutionManagerLayer)); | |
| 117 | + getLocalServerBaseUrl: input.getLocalServerBaseUrl, | |
| 118 | + localConfig: input.localWorkspaceState.loadedConfig.config, | |
| 119 | + workspaceRoot: input.localWorkspaceState.context.workspaceRoot, | |
| 120 | + localWorkspaceState: input.localWorkspaceState, | |
| 121 | + }).pipe(Layer.provide(baseLayer)); | |
| 122 | + | |
| 130 | 123 | const executionResolverLayer = RuntimeExecutionResolverLive({ |
| 131 | − executionResolver: options.executionResolver, | |
| 132 | − resolveSecretMaterial: options.resolveSecretMaterial, | |
| 133 | − }).pipe(Layer.provide(sourceAuthLayer)); | |
| 124 | + executionResolver: input.executionResolver, | |
| 125 | + resolveSecretMaterial: input.resolveSecretMaterial, | |
| 126 | + }).pipe( | |
| 127 | + Layer.provide(Layer.mergeAll(baseLayer, sourceAuthLayer)), | |
| 128 | + ); | |
| 134 | 129 | |
| 135 | 130 | return Layer.mergeAll( |
| 136 | − ControlPlaneStoreLive, | |
| 137 | − liveExecutionManagerLayer, | |
| 131 | + baseLayer, | |
| 138 | 132 | sourceAuthLayer, |
| 139 | 133 | executionResolverLayer, |
| 140 | − ); | |
| 134 | + ) as RuntimeControlPlaneLayer; | |
| 141 | 135 | }; |
| 142 | 136 | |
| 143 | 137 | export type ControlPlaneRuntime = { |
| 144 | − persistence: SqlControlPlanePersistence; | |
| 138 | + persistence: LocalControlPlanePersistence; | |
| 145 | 139 | localInstallation: LocalInstallation; |
| 146 | 140 | runtimeLayer: RuntimeControlPlaneLayer; |
| 147 | 141 | close: () => Promise<void>; |
| 148 | 142 | }; |
| 149 | 143 | |
| 150 | −export type CreateControlPlaneRuntimeOptions = CreateSqlRuntimeOptions & | |
| 151 | − RuntimeControlPlaneOptions; | |
| 144 | +export type CreateControlPlaneRuntimeOptions = RuntimeControlPlaneOptions; | |
| 152 | 145 | |
| 153 | 146 | export const createControlPlaneRuntime = ( |
| 154 | 147 | options: CreateControlPlaneRuntimeOptions, |
| 155 | −): Effect.Effect<ControlPlaneRuntime, SqlPersistenceBootstrapError> => | |
| 148 | +): Effect.Effect<ControlPlaneRuntime, Error> => | |
| 156 | 149 | Effect.gen(function* () { |
| 157 | 150 | const scope = yield* Scope.make(); |
| 158 | − const persistenceAndRowsLayer = SqlControlPlaneRowsLive.pipe( | |
| 159 | − Layer.provideMerge(SqlControlPlanePersistenceLive(options)), | |
| 160 | − ); | |
| 161 | − const baseContext = yield* Layer.buildWithScope( | |
| 162 | − persistenceAndRowsLayer, | |
| 163 | − scope, | |
| 164 | − ).pipe( | |
| 165 | − Effect.catchAll((error) => | |
| 166 | − closeScope(scope).pipe(Effect.zipRight(Effect.fail(error))), | |
| 167 | − ), | |
| 168 | − ); | |
| 169 | − | |
| 170 | − const persistence = Context.get( | |
| 171 | − baseContext, | |
| 172 | − SqlControlPlanePersistenceService, | |
| 173 | − ); | |
| 174 | − const rows = Context.get(baseContext, SqlControlPlaneRowsService); | |
| 175 | 151 | |
| 176 | 152 | const localWorkspaceContext = yield* resolveLocalWorkspaceContext({ |
| 177 | 153 | workspaceRoot: options.workspaceRoot, |
| @@ -182,7 +158,12 @@ export const createControlPlaneRuntime = ( · 4 unmodified lines | ||
| 182 | 158 | ), |
| 183 | 159 | ); |
| 184 | 160 | |
| 185 | − const localInstallation = yield* getOrProvisionLocalInstallation({ | |
| 161 | + const installationStore = LocalInstallationStore; | |
| 162 | + const workspaceConfigStore = LocalWorkspaceConfigStore; | |
| 163 | + const workspaceStateStore = LocalWorkspaceStateStore; | |
| 164 | + const sourceArtifactStore = LocalSourceArtifactStore; | |
| 165 | + | |
| 166 | + const localInstallation = yield* installationStore.getOrProvision({ | |
| 186 | 167 | context: localWorkspaceContext, |
| 187 | 168 | }).pipe( |
| 188 | 169 | Effect.mapError(toLocalRuntimeBootstrapError), |
| @@ -191,7 +172,10 @@ export const createControlPlaneRuntime = ( · 2 unmodified lines | ||
| 191 | 172 | ), |
| 192 | 173 | ); |
| 193 | 174 | |
| 194 | − const loadedLocalConfig = yield* loadLocalExecutorConfig( | |
| 175 | + const persistence = createLocalControlPlanePersistence(localWorkspaceContext); | |
| 176 | + const rows = persistence.rows; | |
| 177 | + | |
| 178 | + const loadedLocalConfig = yield* workspaceConfigStore.load( | |
| 195 | 179 | localWorkspaceContext, |
| 196 | 180 | ).pipe( |
| 197 | 181 | Effect.mapError(toLocalRuntimeBootstrapError), |
| @@ -237,38 +221,42 @@ export const createControlPlaneRuntime = ( · 39 unmodified lines | ||
| 237 | 221 | }, |
| 238 | 222 | }, |
| 239 | 223 | }); |
| 224 | + | |
| 225 | + const runtimeLocalWorkspaceState: RuntimeLocalWorkspaceState = { | |
| 226 | + context: localWorkspaceContext, | |
| 227 | + installation: { | |
| 228 | + workspaceId: localInstallation.workspaceId, | |
| 229 | + accountId: localInstallation.accountId, | |
| 230 | + }, | |
| 231 | + loadedConfig: { | |
| 232 | + ...loadedLocalConfig, | |
| 233 | + config: effectiveLocalConfig, | |
| 234 | + }, | |
| 235 | + }; | |
| 236 | + | |
| 240 | 237 | const executionResolver = |
| 241 | 238 | options.executionResolver ?? |
| 242 | 239 | createWorkspaceExecutionEnvironmentResolver({ |
| 243 | 240 | rows, |
| 244 | 241 | sourceAuthService, |
| 245 | 242 | resolveSecretMaterial, |
| 243 | + workspaceConfigStore, | |
| 244 | + workspaceStateStore, | |
| 245 | + sourceArtifactStore, | |
| 246 | 246 | }); |
| 247 | 247 | |
| 248 | − const runtimeContext = Context.empty().pipe( | |
| 249 | − Context.add(ControlPlaneStore, rows), | |
| 250 | − Context.add(LiveExecutionManagerService, liveExecutionManager), | |
| 251 | − Context.add(RuntimeLocalWorkspaceService, { | |
| 252 | − context: localWorkspaceContext, | |
| 253 | − installation: { | |
| 254 | − workspaceId: localInstallation.workspaceId, | |
| 255 | − accountId: localInstallation.accountId, | |
| 256 | − }, | |
| 257 | − loadedConfig: { | |
| 258 | − ...loadedLocalConfig, | |
| 259 | − config: effectiveLocalConfig, | |
| 260 | − }, | |
| 261 | − }), | |
| 262 | − Context.add(RuntimeSourceAuthServiceTag, sourceAuthService), | |
| 263 | − Context.add(RuntimeExecutionResolverService, executionResolver), | |
| 264 | − ); | |
| 265 | − const concreteRuntimeLayer = createRuntimeLayerFromContext(runtimeContext); | |
| 248 | + const concreteRuntimeLayer = createRuntimeControlPlaneLayer({ | |
| 249 | + ...options, | |
| 250 | + store: rows, | |
| 251 | + localWorkspaceState: runtimeLocalWorkspaceState, | |
| 252 | + resolveSecretMaterial, | |
| 253 | + executionResolver, | |
| 254 | + }); | |
| 266 | 255 | |
| 267 | 256 | return { |
| 268 | 257 | persistence, |
| 269 | 258 | localInstallation, |
| 270 | 259 | runtimeLayer: concreteRuntimeLayer, |
| 271 | − close: () => | |
| 272 | − Effect.runPromise(Scope.close(scope, Exit.void)).catch(() => {}), | |
| 260 | + close: () => Effect.runPromise(Scope.close(scope, Exit.void)).catch(() => {}), | |
| 273 | 261 | }; |
| 274 | 262 | }); |
| @@ -9,7 +9,7 @@ describe("live-execution", () => { · 8 unmodified lines | ||
| 9 | 9 | content: { |
| 10 | 10 | authKind: "bearer", |
| 11 | 11 | tokenRef: { |
| 12 | − providerId: "postgres", | |
| 12 | + providerId: "local", | |
| 13 | 13 | handle: "sec_123", |
| 14 | 14 | }, |
| 15 | 15 | }, |
| @@ -2,7 +2,6 @@ import type { · 1 unmodified line | ||
| 2 | 2 | ElicitationResponse, |
| 3 | 3 | OnElicitation, |
| 4 | 4 | } from "@executor/codemode-core"; |
| 5 | −import { type SqlControlPlaneRows } from "#persistence"; | |
| 6 | 5 | import { |
| 7 | 6 | ExecutionInteractionIdSchema, |
| 8 | 7 | type Execution, |
| @@ -13,6 +12,8 @@ import * as Deferred from "effect/Deferred"; · 4 unmodified lines | ||
| 13 | 12 | import * as Effect from "effect/Effect"; |
| 14 | 13 | import * as Layer from "effect/Layer"; |
| 15 | 14 | |
| 15 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 16 | + | |
| 16 | 17 | type VisibleExecutionState = |
| 17 | 18 | | "waiting_for_interaction" |
| 18 | 19 | | "completed" |
| @@ -35,7 +36,7 @@ type LiveExecutionManagerShape = { · 16 unmodified lines | ||
| 35 | 36 | executionId: Execution["id"], |
| 36 | 37 | ) => Effect.Effect<Deferred.Deferred<VisibleExecutionState>>; |
| 37 | 38 | createOnElicitation: (input: { |
| 38 | − rows: SqlControlPlaneRows; | |
| 39 | + rows: ControlPlaneStoreShape; | |
| 39 | 40 | executionId: Execution["id"]; |
| 40 | 41 | }) => OnElicitation; |
| 41 | 42 | resolveInteraction: (input: { |
| @@ -0,0 +1,1054 @@ | ||
| 1 | +import { randomUUID } from "node:crypto"; | |
| 2 | +import { existsSync } from "node:fs"; | |
| 3 | +import { mkdir, readFile, rename, writeFile } from "node:fs/promises"; | |
| 4 | +import { dirname, join, resolve } from "node:path"; | |
| 5 | + | |
| 6 | +import { | |
| 7 | + AuthArtifactSchema, | |
| 8 | + AuthLeaseSchema, | |
| 9 | + type AuthArtifact, | |
| 10 | + type AuthLease, | |
| 11 | + type Execution, | |
| 12 | + type ExecutionInteraction, | |
| 13 | + type ExecutionStep, | |
| 14 | + ExecutionInteractionSchema, | |
| 15 | + ExecutionSchema, | |
| 16 | + ExecutionStepSchema, | |
| 17 | + SecretMaterialSchema, | |
| 18 | + type SecretMaterial, | |
| 19 | + type SourceAuthSession, | |
| 20 | + SourceAuthSessionSchema, | |
| 21 | + type WorkspaceSourceOauthClient, | |
| 22 | + WorkspaceSourceOauthClientSchema, | |
| 23 | +} from "#schema"; | |
| 24 | +import * as Effect from "effect/Effect"; | |
| 25 | +import * as Option from "effect/Option"; | |
| 26 | +import * as Schema from "effect/Schema"; | |
| 27 | + | |
| 28 | +import { authArtifactSecretMaterialRefs } from "./auth-artifacts"; | |
| 29 | +import type { ResolvedLocalWorkspaceContext } from "./local-config"; | |
| 30 | +import { loadLocalExecutorConfig } from "./local-config"; | |
| 31 | +import { | |
| 32 | + LocalFileSystemError, | |
| 33 | + unknownLocalErrorDetails, | |
| 34 | +} from "./local-errors"; | |
| 35 | + | |
| 36 | +const LOCAL_CONTROL_PLANE_STATE_VERSION = 1 as const; | |
| 37 | +const LOCAL_CONTROL_PLANE_STATE_BASENAME = "control-plane-state.json"; | |
| 38 | + | |
| 39 | +const LocalControlPlaneStateSchema = Schema.Struct({ | |
| 40 | + version: Schema.Literal(LOCAL_CONTROL_PLANE_STATE_VERSION), | |
| 41 | + authArtifacts: Schema.Array(AuthArtifactSchema), | |
| 42 | + authLeases: Schema.Array(AuthLeaseSchema), | |
| 43 | + sourceOauthClients: Schema.Array(WorkspaceSourceOauthClientSchema), | |
| 44 | + sourceAuthSessions: Schema.Array(SourceAuthSessionSchema), | |
| 45 | + secretMaterials: Schema.Array(SecretMaterialSchema), | |
| 46 | + executions: Schema.Array(ExecutionSchema), | |
| 47 | + executionInteractions: Schema.Array(ExecutionInteractionSchema), | |
| 48 | + executionSteps: Schema.Array(ExecutionStepSchema), | |
| 49 | +}); | |
| 50 | + | |
| 51 | +export type LocalControlPlaneState = typeof LocalControlPlaneStateSchema.Type; | |
| 52 | + | |
| 53 | +export type LocalControlPlanePersistence = { | |
| 54 | + rows: LocalControlPlaneStore; | |
| 55 | + close: () => Promise<void>; | |
| 56 | +}; | |
| 57 | + | |
| 58 | +const decodeLocalControlPlaneState = Schema.decodeUnknownSync( | |
| 59 | + LocalControlPlaneStateSchema, | |
| 60 | +); | |
| 61 | + | |
| 62 | +const defaultLocalControlPlaneState = (): LocalControlPlaneState => ({ | |
| 63 | + version: LOCAL_CONTROL_PLANE_STATE_VERSION, | |
| 64 | + authArtifacts: [], | |
| 65 | + authLeases: [], | |
| 66 | + sourceOauthClients: [], | |
| 67 | + sourceAuthSessions: [], | |
| 68 | + secretMaterials: [], | |
| 69 | + executions: [], | |
| 70 | + executionInteractions: [], | |
| 71 | + executionSteps: [], | |
| 72 | +}); | |
| 73 | + | |
| 74 | +const cloneValue = <T>(value: T): T => | |
| 75 | + JSON.parse(JSON.stringify(value)) as T; | |
| 76 | + | |
| 77 | +const mapFileSystemError = (path: string, action: string) => (cause: unknown) => | |
| 78 | + new LocalFileSystemError({ | |
| 79 | + message: `Failed to ${action} ${path}: ${unknownLocalErrorDetails(cause)}`, | |
| 80 | + action, | |
| 81 | + path, | |
| 82 | + details: unknownLocalErrorDetails(cause), | |
| 83 | + }); | |
| 84 | + | |
| 85 | +const actorEquals = ( | |
| 86 | + left: string | null | undefined, | |
| 87 | + right: string | null | undefined, | |
| 88 | +): boolean => (left ?? null) === (right ?? null); | |
| 89 | + | |
| 90 | +const sortByUpdatedAtAndIdAsc = <T extends { updatedAt: number; id: string }>( | |
| 91 | + values: readonly T[], | |
| 92 | +): T[] => | |
| 93 | + [...values].sort((left, right) => | |
| 94 | + left.updatedAt - right.updatedAt || left.id.localeCompare(right.id), | |
| 95 | + ); | |
| 96 | + | |
| 97 | +const sortByUpdatedAtAndIdDesc = <T extends { updatedAt: number; id: string }>( | |
| 98 | + values: readonly T[], | |
| 99 | +): T[] => | |
| 100 | + [...values].sort((left, right) => | |
| 101 | + right.updatedAt - left.updatedAt || right.id.localeCompare(left.id), | |
| 102 | + ); | |
| 103 | + | |
| 104 | +const localControlPlaneStatePath = ( | |
| 105 | + context: ResolvedLocalWorkspaceContext, | |
| 106 | +): string => join(context.stateDirectory, LOCAL_CONTROL_PLANE_STATE_BASENAME); | |
| 107 | + | |
| 108 | +const readStateFromDisk = async ( | |
| 109 | + context: ResolvedLocalWorkspaceContext, | |
| 110 | +): Promise<LocalControlPlaneState> => { | |
| 111 | + const path = localControlPlaneStatePath(context); | |
| 112 | + if (!existsSync(path)) { | |
| 113 | + return defaultLocalControlPlaneState(); | |
| 114 | + } | |
| 115 | + | |
| 116 | + const content = await readFile(path, "utf8"); | |
| 117 | + return decodeLocalControlPlaneState(JSON.parse(content) as unknown); | |
| 118 | +}; | |
| 119 | + | |
| 120 | +const writeStateToDisk = async ( | |
| 121 | + context: ResolvedLocalWorkspaceContext, | |
| 122 | + state: LocalControlPlaneState, | |
| 123 | +): Promise<void> => { | |
| 124 | + const path = localControlPlaneStatePath(context); | |
| 125 | + await mkdir(dirname(path), { recursive: true }); | |
| 126 | + const tempPath = `${path}.${randomUUID()}.tmp`; | |
| 127 | + await writeFile(tempPath, `${JSON.stringify(state, null, 2)}\n`, "utf8"); | |
| 128 | + await rename(tempPath, path); | |
| 129 | +}; | |
| 130 | + | |
| 131 | +export const loadLocalControlPlaneState = ( | |
| 132 | + context: ResolvedLocalWorkspaceContext, | |
| 133 | +): Effect.Effect<LocalControlPlaneState, LocalFileSystemError> => | |
| 134 | + Effect.tryPromise({ | |
| 135 | + try: () => readStateFromDisk(context), | |
| 136 | + catch: mapFileSystemError( | |
| 137 | + localControlPlaneStatePath(context), | |
| 138 | + "read control plane state", | |
| 139 | + ), | |
| 140 | + }); | |
| 141 | + | |
| 142 | +export const writeLocalControlPlaneState = (input: { | |
| 143 | + context: ResolvedLocalWorkspaceContext; | |
| 144 | + state: LocalControlPlaneState; | |
| 145 | +}): Effect.Effect<void, LocalFileSystemError> => | |
| 146 | + Effect.tryPromise({ | |
| 147 | + try: () => writeStateToDisk(input.context, input.state), | |
| 148 | + catch: mapFileSystemError( | |
| 149 | + localControlPlaneStatePath(input.context), | |
| 150 | + "write control plane state", | |
| 151 | + ), | |
| 152 | + }); | |
| 153 | + | |
| 154 | +const mergeById = <T extends { id: string }>( | |
| 155 | + current: readonly T[], | |
| 156 | + imported: readonly T[], | |
| 157 | +): T[] => { | |
| 158 | + const merged = new Map<string, T>(); | |
| 159 | + | |
| 160 | + for (const item of imported) { | |
| 161 | + merged.set(item.id, cloneValue(item)); | |
| 162 | + } | |
| 163 | + | |
| 164 | + for (const item of current) { | |
| 165 | + merged.set(item.id, cloneValue(item)); | |
| 166 | + } | |
| 167 | + | |
| 168 | + return [...merged.values()]; | |
| 169 | +}; | |
| 170 | + | |
| 171 | +const mergeAuthLeases = ( | |
| 172 | + current: readonly AuthLease[], | |
| 173 | + imported: readonly AuthLease[], | |
| 174 | +): AuthLease[] => { | |
| 175 | + const merged = new Map<string, AuthLease>(); | |
| 176 | + | |
| 177 | + for (const lease of imported) { | |
| 178 | + merged.set(lease.authArtifactId, cloneValue(lease)); | |
| 179 | + } | |
| 180 | + | |
| 181 | + for (const lease of current) { | |
| 182 | + merged.set(lease.authArtifactId, cloneValue(lease)); | |
| 183 | + } | |
| 184 | + | |
| 185 | + return [...merged.values()]; | |
| 186 | +}; | |
| 187 | + | |
| 188 | +const mergeAuthArtifacts = ( | |
| 189 | + current: readonly AuthArtifact[], | |
| 190 | + imported: readonly AuthArtifact[], | |
| 191 | +): AuthArtifact[] => { | |
| 192 | + const merged = new Map<string, AuthArtifact>(); | |
| 193 | + | |
| 194 | + for (const artifact of imported) { | |
| 195 | + merged.set( | |
| 196 | + [ | |
| 197 | + artifact.workspaceId, | |
| 198 | + artifact.sourceId, | |
| 199 | + artifact.actorAccountId ?? "", | |
| 200 | + artifact.slot, | |
| 201 | + ].join("::"), | |
| 202 | + cloneValue(artifact), | |
| 203 | + ); | |
| 204 | + } | |
| 205 | + | |
| 206 | + for (const artifact of current) { | |
| 207 | + merged.set( | |
| 208 | + [ | |
| 209 | + artifact.workspaceId, | |
| 210 | + artifact.sourceId, | |
| 211 | + artifact.actorAccountId ?? "", | |
| 212 | + artifact.slot, | |
| 213 | + ].join("::"), | |
| 214 | + cloneValue(artifact), | |
| 215 | + ); | |
| 216 | + } | |
| 217 | + | |
| 218 | + return [...merged.values()]; | |
| 219 | +}; | |
| 220 | + | |
| 221 | +const mergeSourceOauthClients = ( | |
| 222 | + current: readonly WorkspaceSourceOauthClient[], | |
| 223 | + imported: readonly WorkspaceSourceOauthClient[], | |
| 224 | +): WorkspaceSourceOauthClient[] => { | |
| 225 | + const merged = new Map<string, WorkspaceSourceOauthClient>(); | |
| 226 | + | |
| 227 | + for (const oauthClient of imported) { | |
| 228 | + merged.set( | |
| 229 | + [oauthClient.workspaceId, oauthClient.sourceId, oauthClient.providerKey].join( | |
| 230 | + "::", | |
| 231 | + ), | |
| 232 | + cloneValue(oauthClient), | |
| 233 | + ); | |
| 234 | + } | |
| 235 | + | |
| 236 | + for (const oauthClient of current) { | |
| 237 | + merged.set( | |
| 238 | + [oauthClient.workspaceId, oauthClient.sourceId, oauthClient.providerKey].join( | |
| 239 | + "::", | |
| 240 | + ), | |
| 241 | + cloneValue(oauthClient), | |
| 242 | + ); | |
| 243 | + } | |
| 244 | + | |
| 245 | + return [...merged.values()]; | |
| 246 | +}; | |
| 247 | + | |
| 248 | +export const mergeImportedLocalControlPlaneState = (input: { | |
| 249 | + current: LocalControlPlaneState; | |
| 250 | + imported: Partial<Omit<LocalControlPlaneState, "version">>; | |
| 251 | +}): LocalControlPlaneState => ({ | |
| 252 | + version: LOCAL_CONTROL_PLANE_STATE_VERSION, | |
| 253 | + authArtifacts: mergeAuthArtifacts( | |
| 254 | + input.current.authArtifacts, | |
| 255 | + input.imported.authArtifacts ?? [], | |
| 256 | + ), | |
| 257 | + authLeases: mergeAuthLeases( | |
| 258 | + input.current.authLeases, | |
| 259 | + input.imported.authLeases ?? [], | |
| 260 | + ), | |
| 261 | + sourceOauthClients: mergeSourceOauthClients( | |
| 262 | + input.current.sourceOauthClients, | |
| 263 | + input.imported.sourceOauthClients ?? [], | |
| 264 | + ), | |
| 265 | + sourceAuthSessions: mergeById( | |
| 266 | + input.current.sourceAuthSessions, | |
| 267 | + input.imported.sourceAuthSessions ?? [], | |
| 268 | + ), | |
| 269 | + secretMaterials: mergeById( | |
| 270 | + input.current.secretMaterials, | |
| 271 | + input.imported.secretMaterials ?? [], | |
| 272 | + ), | |
| 273 | + executions: mergeById(input.current.executions, input.imported.executions ?? []), | |
| 274 | + executionInteractions: mergeById( | |
| 275 | + input.current.executionInteractions, | |
| 276 | + input.imported.executionInteractions ?? [], | |
| 277 | + ), | |
| 278 | + executionSteps: mergeById( | |
| 279 | + input.current.executionSteps, | |
| 280 | + input.imported.executionSteps ?? [], | |
| 281 | + ), | |
| 282 | +}); | |
| 283 | + | |
| 284 | +type StateMutationResult<A> = { | |
| 285 | + state: LocalControlPlaneState; | |
| 286 | + value: A; | |
| 287 | +}; | |
| 288 | + | |
| 289 | +const createStateManager = (context: ResolvedLocalWorkspaceContext) => { | |
| 290 | + let cache: LocalControlPlaneState | null = null; | |
| 291 | + let mutationQueue: Promise<void> = Promise.resolve(); | |
| 292 | + | |
| 293 | + const ensureLoaded = async (): Promise<LocalControlPlaneState> => { | |
| 294 | + if (cache !== null) { | |
| 295 | + return cache; | |
| 296 | + } | |
| 297 | + | |
| 298 | + cache = await readStateFromDisk(context); | |
| 299 | + return cache; | |
| 300 | + }; | |
| 301 | + | |
| 302 | + const read = <A>( | |
| 303 | + operation: (state: LocalControlPlaneState) => A | Promise<A>, | |
| 304 | + ): Effect.Effect<A, LocalFileSystemError> => | |
| 305 | + Effect.tryPromise({ | |
| 306 | + try: async () => operation(cloneValue(await ensureLoaded())), | |
| 307 | + catch: mapFileSystemError( | |
| 308 | + localControlPlaneStatePath(context), | |
| 309 | + "read control plane state", | |
| 310 | + ), | |
| 311 | + }); | |
| 312 | + | |
| 313 | + const mutate = <A>( | |
| 314 | + operation: ( | |
| 315 | + state: LocalControlPlaneState, | |
| 316 | + ) => StateMutationResult<A> | Promise<StateMutationResult<A>>, | |
| 317 | + ): Effect.Effect<A, LocalFileSystemError> => | |
| 318 | + Effect.tryPromise({ | |
| 319 | + try: async () => { | |
| 320 | + let value!: A; | |
| 321 | + let failure: unknown = null; | |
| 322 | + | |
| 323 | + mutationQueue = mutationQueue.then(async () => { | |
| 324 | + try { | |
| 325 | + const current = cloneValue(await ensureLoaded()); | |
| 326 | + const result = await operation(current); | |
| 327 | + cache = result.state; | |
| 328 | + value = result.value; | |
| 329 | + await writeStateToDisk(context, cache); | |
| 330 | + } catch (cause) { | |
| 331 | + failure = cause; | |
| 332 | + } | |
| 333 | + }); | |
| 334 | + | |
| 335 | + await mutationQueue; | |
| 336 | + | |
| 337 | + if (failure !== null) { | |
| 338 | + throw failure; | |
| 339 | + } | |
| 340 | + | |
| 341 | + return value; | |
| 342 | + }, | |
| 343 | + catch: mapFileSystemError( | |
| 344 | + localControlPlaneStatePath(context), | |
| 345 | + "write control plane state", | |
| 346 | + ), | |
| 347 | + }); | |
| 348 | + | |
| 349 | + return { | |
| 350 | + read, | |
| 351 | + mutate, | |
| 352 | + }; | |
| 353 | +}; | |
| 354 | + | |
| 355 | +export const createLocalControlPlaneStore = ( | |
| 356 | + context: ResolvedLocalWorkspaceContext, | |
| 357 | +) => { | |
| 358 | + const stateManager = createStateManager(context); | |
| 359 | + | |
| 360 | + return { | |
| 361 | + authArtifacts: { | |
| 362 | + listByWorkspaceId: (workspaceId: AuthArtifact["workspaceId"]) => | |
| 363 | + stateManager.read((state) => | |
| 364 | + sortByUpdatedAtAndIdAsc( | |
| 365 | + state.authArtifacts.filter((artifact) => artifact.workspaceId === workspaceId), | |
| 366 | + ), | |
| 367 | + ), | |
| 368 | + | |
| 369 | + listByWorkspaceAndSourceId: (input: { | |
| 370 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 371 | + sourceId: AuthArtifact["sourceId"]; | |
| 372 | + }) => | |
| 373 | + stateManager.read((state) => | |
| 374 | + sortByUpdatedAtAndIdAsc( | |
| 375 | + state.authArtifacts.filter( | |
| 376 | + (artifact) => | |
| 377 | + artifact.workspaceId === input.workspaceId | |
| 378 | + && artifact.sourceId === input.sourceId, | |
| 379 | + ), | |
| 380 | + ), | |
| 381 | + ), | |
| 382 | + | |
| 383 | + getByWorkspaceSourceAndActor: (input: { | |
| 384 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 385 | + sourceId: AuthArtifact["sourceId"]; | |
| 386 | + actorAccountId: AuthArtifact["actorAccountId"]; | |
| 387 | + slot: AuthArtifact["slot"]; | |
| 388 | + }) => | |
| 389 | + stateManager.read((state) => { | |
| 390 | + const artifact = state.authArtifacts.find( | |
| 391 | + (candidate) => | |
| 392 | + candidate.workspaceId === input.workspaceId | |
| 393 | + && candidate.sourceId === input.sourceId | |
| 394 | + && candidate.slot === input.slot | |
| 395 | + && actorEquals(candidate.actorAccountId, input.actorAccountId), | |
| 396 | + ); | |
| 397 | + | |
| 398 | + return artifact ? Option.some(cloneValue(artifact)) : Option.none<AuthArtifact>(); | |
| 399 | + }), | |
| 400 | + | |
| 401 | + upsert: (artifact: AuthArtifact) => | |
| 402 | + stateManager.mutate((state) => { | |
| 403 | + const nextArtifacts = state.authArtifacts.filter( | |
| 404 | + (candidate) => | |
| 405 | + !( | |
| 406 | + candidate.workspaceId === artifact.workspaceId | |
| 407 | + && candidate.sourceId === artifact.sourceId | |
| 408 | + && candidate.slot === artifact.slot | |
| 409 | + && actorEquals(candidate.actorAccountId, artifact.actorAccountId) | |
| 410 | + ), | |
| 411 | + ); | |
| 412 | + nextArtifacts.push(cloneValue(artifact)); | |
| 413 | + | |
| 414 | + return { | |
| 415 | + state: { | |
| 416 | + ...state, | |
| 417 | + authArtifacts: nextArtifacts, | |
| 418 | + }, | |
| 419 | + value: undefined, | |
| 420 | + } satisfies StateMutationResult<void>; | |
| 421 | + }), | |
| 422 | + | |
| 423 | + removeByWorkspaceSourceAndActor: (input: { | |
| 424 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 425 | + sourceId: AuthArtifact["sourceId"]; | |
| 426 | + actorAccountId: AuthArtifact["actorAccountId"]; | |
| 427 | + slot?: AuthArtifact["slot"]; | |
| 428 | + }) => | |
| 429 | + stateManager.mutate((state) => { | |
| 430 | + const nextArtifacts = state.authArtifacts.filter( | |
| 431 | + (candidate) => | |
| 432 | + candidate.workspaceId !== input.workspaceId | |
| 433 | + || candidate.sourceId !== input.sourceId | |
| 434 | + || !actorEquals(candidate.actorAccountId, input.actorAccountId) | |
| 435 | + || (input.slot !== undefined && candidate.slot !== input.slot), | |
| 436 | + ); | |
| 437 | + | |
| 438 | + return { | |
| 439 | + state: { | |
| 440 | + ...state, | |
| 441 | + authArtifacts: nextArtifacts, | |
| 442 | + }, | |
| 443 | + value: nextArtifacts.length !== state.authArtifacts.length, | |
| 444 | + } satisfies StateMutationResult<boolean>; | |
| 445 | + }), | |
| 446 | + | |
| 447 | + removeByWorkspaceAndSourceId: (input: { | |
| 448 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 449 | + sourceId: AuthArtifact["sourceId"]; | |
| 450 | + }) => | |
| 451 | + stateManager.mutate((state) => { | |
| 452 | + const nextArtifacts = state.authArtifacts.filter( | |
| 453 | + (candidate) => | |
| 454 | + candidate.workspaceId !== input.workspaceId | |
| 455 | + || candidate.sourceId !== input.sourceId, | |
| 456 | + ); | |
| 457 | + | |
| 458 | + return { | |
| 459 | + state: { | |
| 460 | + ...state, | |
| 461 | + authArtifacts: nextArtifacts, | |
| 462 | + }, | |
| 463 | + value: state.authArtifacts.length - nextArtifacts.length, | |
| 464 | + } satisfies StateMutationResult<number>; | |
| 465 | + }), | |
| 466 | + }, | |
| 467 | + | |
| 468 | + authLeases: { | |
| 469 | + listAll: () => | |
| 470 | + stateManager.read((state) => sortByUpdatedAtAndIdAsc(state.authLeases)), | |
| 471 | + | |
| 472 | + getByAuthArtifactId: (authArtifactId: AuthLease["authArtifactId"]) => | |
| 473 | + stateManager.read((state) => { | |
| 474 | + const lease = state.authLeases.find( | |
| 475 | + (candidate) => candidate.authArtifactId === authArtifactId, | |
| 476 | + ); | |
| 477 | + return lease ? Option.some(cloneValue(lease)) : Option.none<AuthLease>(); | |
| 478 | + }), | |
| 479 | + | |
| 480 | + upsert: (lease: AuthLease) => | |
| 481 | + stateManager.mutate((state) => { | |
| 482 | + const nextLeases = state.authLeases.filter( | |
| 483 | + (candidate) => candidate.authArtifactId !== lease.authArtifactId, | |
| 484 | + ); | |
| 485 | + nextLeases.push(cloneValue(lease)); | |
| 486 | + | |
| 487 | + return { | |
| 488 | + state: { | |
| 489 | + ...state, | |
| 490 | + authLeases: nextLeases, | |
| 491 | + }, | |
| 492 | + value: undefined, | |
| 493 | + } satisfies StateMutationResult<void>; | |
| 494 | + }), | |
| 495 | + | |
| 496 | + removeByAuthArtifactId: (authArtifactId: AuthLease["authArtifactId"]) => | |
| 497 | + stateManager.mutate((state) => { | |
| 498 | + const nextLeases = state.authLeases.filter( | |
| 499 | + (candidate) => candidate.authArtifactId !== authArtifactId, | |
| 500 | + ); | |
| 501 | + | |
| 502 | + return { | |
| 503 | + state: { | |
| 504 | + ...state, | |
| 505 | + authLeases: nextLeases, | |
| 506 | + }, | |
| 507 | + value: nextLeases.length !== state.authLeases.length, | |
| 508 | + } satisfies StateMutationResult<boolean>; | |
| 509 | + }), | |
| 510 | + }, | |
| 511 | + | |
| 512 | + sourceOauthClients: { | |
| 513 | + getByWorkspaceSourceAndProvider: (input: { | |
| 514 | + workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 515 | + sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 516 | + providerKey: string; | |
| 517 | + }) => | |
| 518 | + stateManager.read((state) => { | |
| 519 | + const oauthClient = state.sourceOauthClients.find( | |
| 520 | + (candidate) => | |
| 521 | + candidate.workspaceId === input.workspaceId | |
| 522 | + && candidate.sourceId === input.sourceId | |
| 523 | + && candidate.providerKey === input.providerKey, | |
| 524 | + ); | |
| 525 | + | |
| 526 | + return oauthClient | |
| 527 | + ? Option.some(cloneValue(oauthClient)) | |
| 528 | + : Option.none<WorkspaceSourceOauthClient>(); | |
| 529 | + }), | |
| 530 | + | |
| 531 | + upsert: (oauthClient: WorkspaceSourceOauthClient) => | |
| 532 | + stateManager.mutate((state) => { | |
| 533 | + const nextOauthClients = state.sourceOauthClients.filter( | |
| 534 | + (candidate) => | |
| 535 | + !( | |
| 536 | + candidate.workspaceId === oauthClient.workspaceId | |
| 537 | + && candidate.sourceId === oauthClient.sourceId | |
| 538 | + && candidate.providerKey === oauthClient.providerKey | |
| 539 | + ), | |
| 540 | + ); | |
| 541 | + nextOauthClients.push(cloneValue(oauthClient)); | |
| 542 | + | |
| 543 | + return { | |
| 544 | + state: { | |
| 545 | + ...state, | |
| 546 | + sourceOauthClients: nextOauthClients, | |
| 547 | + }, | |
| 548 | + value: undefined, | |
| 549 | + } satisfies StateMutationResult<void>; | |
| 550 | + }), | |
| 551 | + | |
| 552 | + removeByWorkspaceAndSourceId: (input: { | |
| 553 | + workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 554 | + sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 555 | + }) => | |
| 556 | + stateManager.mutate((state) => { | |
| 557 | + const nextOauthClients = state.sourceOauthClients.filter( | |
| 558 | + (candidate) => | |
| 559 | + candidate.workspaceId !== input.workspaceId | |
| 560 | + || candidate.sourceId !== input.sourceId, | |
| 561 | + ); | |
| 562 | + | |
| 563 | + return { | |
| 564 | + state: { | |
| 565 | + ...state, | |
| 566 | + sourceOauthClients: nextOauthClients, | |
| 567 | + }, | |
| 568 | + value: state.sourceOauthClients.length - nextOauthClients.length, | |
| 569 | + } satisfies StateMutationResult<number>; | |
| 570 | + }), | |
| 571 | + }, | |
| 572 | + | |
| 573 | + sourceAuthSessions: { | |
| 574 | + listAll: () => | |
| 575 | + stateManager.read((state) => sortByUpdatedAtAndIdAsc(state.sourceAuthSessions)), | |
| 576 | + | |
| 577 | + listByWorkspaceId: (workspaceId: SourceAuthSession["workspaceId"]) => | |
| 578 | + stateManager.read((state) => | |
| 579 | + sortByUpdatedAtAndIdAsc( | |
| 580 | + state.sourceAuthSessions.filter( | |
| 581 | + (session) => session.workspaceId === workspaceId, | |
| 582 | + ), | |
| 583 | + ), | |
| 584 | + ), | |
| 585 | + | |
| 586 | + getById: (id: SourceAuthSession["id"]) => | |
| 587 | + stateManager.read((state) => { | |
| 588 | + const session = state.sourceAuthSessions.find( | |
| 589 | + (candidate) => candidate.id === id, | |
| 590 | + ); | |
| 591 | + return session | |
| 592 | + ? Option.some(cloneValue(session)) | |
| 593 | + : Option.none<SourceAuthSession>(); | |
| 594 | + }), | |
| 595 | + | |
| 596 | + getByState: (stateValue: SourceAuthSession["state"]) => | |
| 597 | + stateManager.read((state) => { | |
| 598 | + const session = state.sourceAuthSessions.find( | |
| 599 | + (candidate) => candidate.state === stateValue, | |
| 600 | + ); | |
| 601 | + return session | |
| 602 | + ? Option.some(cloneValue(session)) | |
| 603 | + : Option.none<SourceAuthSession>(); | |
| 604 | + }), | |
| 605 | + | |
| 606 | + getPendingByWorkspaceSourceAndActor: (input: { | |
| 607 | + workspaceId: SourceAuthSession["workspaceId"]; | |
| 608 | + sourceId: SourceAuthSession["sourceId"]; | |
| 609 | + actorAccountId: SourceAuthSession["actorAccountId"]; | |
| 610 | + credentialSlot?: SourceAuthSession["credentialSlot"]; | |
| 611 | + }) => | |
| 612 | + stateManager.read((state) => { | |
| 613 | + const session = sortByUpdatedAtAndIdAsc( | |
| 614 | + state.sourceAuthSessions.filter( | |
| 615 | + (candidate) => | |
| 616 | + candidate.workspaceId === input.workspaceId | |
| 617 | + && candidate.sourceId === input.sourceId | |
| 618 | + && candidate.status === "pending" | |
| 619 | + && actorEquals(candidate.actorAccountId, input.actorAccountId) | |
| 620 | + && (input.credentialSlot === undefined | |
| 621 | + || candidate.credentialSlot === input.credentialSlot), | |
| 622 | + ), | |
| 623 | + )[0] ?? null; | |
| 624 | + | |
| 625 | + return session | |
| 626 | + ? Option.some(cloneValue(session)) | |
| 627 | + : Option.none<SourceAuthSession>(); | |
| 628 | + }), | |
| 629 | + | |
| 630 | + insert: (session: SourceAuthSession) => | |
| 631 | + stateManager.mutate((state) => ({ | |
| 632 | + state: { | |
| 633 | + ...state, | |
| 634 | + sourceAuthSessions: [...state.sourceAuthSessions, cloneValue(session)], | |
| 635 | + }, | |
| 636 | + value: undefined, | |
| 637 | + } satisfies StateMutationResult<void>)), | |
| 638 | + | |
| 639 | + update: ( | |
| 640 | + id: SourceAuthSession["id"], | |
| 641 | + patch: Partial<Omit<SourceAuthSession, "id" | "workspaceId" | "sourceId" | "createdAt">>, | |
| 642 | + ) => | |
| 643 | + stateManager.mutate((state) => { | |
| 644 | + let updated: SourceAuthSession | null = null; | |
| 645 | + const nextSessions = state.sourceAuthSessions.map((session) => { | |
| 646 | + if (session.id !== id) { | |
| 647 | + return session; | |
| 648 | + } | |
| 649 | + | |
| 650 | + updated = { | |
| 651 | + ...session, | |
| 652 | + ...cloneValue(patch), | |
| 653 | + } satisfies SourceAuthSession; | |
| 654 | + return updated; | |
| 655 | + }); | |
| 656 | + | |
| 657 | + return { | |
| 658 | + state: { | |
| 659 | + ...state, | |
| 660 | + sourceAuthSessions: nextSessions, | |
| 661 | + }, | |
| 662 | + value: updated ? Option.some(cloneValue(updated)) : Option.none<SourceAuthSession>(), | |
| 663 | + } satisfies StateMutationResult<Option.Option<SourceAuthSession>>; | |
| 664 | + }), | |
| 665 | + | |
| 666 | + upsert: (session: SourceAuthSession) => | |
| 667 | + stateManager.mutate((state) => { | |
| 668 | + const nextSessions = state.sourceAuthSessions.filter( | |
| 669 | + (candidate) => candidate.id !== session.id, | |
| 670 | + ); | |
| 671 | + nextSessions.push(cloneValue(session)); | |
| 672 | + | |
| 673 | + return { | |
| 674 | + state: { | |
| 675 | + ...state, | |
| 676 | + sourceAuthSessions: nextSessions, | |
| 677 | + }, | |
| 678 | + value: undefined, | |
| 679 | + } satisfies StateMutationResult<void>; | |
| 680 | + }), | |
| 681 | + | |
| 682 | + removeByWorkspaceAndSourceId: ( | |
| 683 | + workspaceId: SourceAuthSession["workspaceId"], | |
| 684 | + sourceId: SourceAuthSession["sourceId"], | |
| 685 | + ) => | |
| 686 | + stateManager.mutate((state) => { | |
| 687 | + const nextSessions = state.sourceAuthSessions.filter( | |
| 688 | + (candidate) => | |
| 689 | + candidate.workspaceId !== workspaceId || candidate.sourceId !== sourceId, | |
| 690 | + ); | |
| 691 | + | |
| 692 | + return { | |
| 693 | + state: { | |
| 694 | + ...state, | |
| 695 | + sourceAuthSessions: nextSessions, | |
| 696 | + }, | |
| 697 | + value: nextSessions.length !== state.sourceAuthSessions.length, | |
| 698 | + } satisfies StateMutationResult<boolean>; | |
| 699 | + }), | |
| 700 | + }, | |
| 701 | + | |
| 702 | + secretMaterials: { | |
| 703 | + getById: (id: SecretMaterial["id"]) => | |
| 704 | + stateManager.read((state) => { | |
| 705 | + const material = state.secretMaterials.find( | |
| 706 | + (candidate) => candidate.id === id, | |
| 707 | + ); | |
| 708 | + return material | |
| 709 | + ? Option.some(cloneValue(material)) | |
| 710 | + : Option.none<SecretMaterial>(); | |
| 711 | + }), | |
| 712 | + | |
| 713 | + listAll: () => | |
| 714 | + stateManager.read((state) => sortByUpdatedAtAndIdDesc(state.secretMaterials)), | |
| 715 | + | |
| 716 | + upsert: (material: SecretMaterial) => | |
| 717 | + stateManager.mutate((state) => { | |
| 718 | + const nextMaterials = state.secretMaterials.filter( | |
| 719 | + (candidate) => candidate.id !== material.id, | |
| 720 | + ); | |
| 721 | + nextMaterials.push(cloneValue(material)); | |
| 722 | + | |
| 723 | + return { | |
| 724 | + state: { | |
| 725 | + ...state, | |
| 726 | + secretMaterials: nextMaterials, | |
| 727 | + }, | |
| 728 | + value: undefined, | |
| 729 | + } satisfies StateMutationResult<void>; | |
| 730 | + }), | |
| 731 | + | |
| 732 | + updateById: ( | |
| 733 | + id: SecretMaterial["id"], | |
| 734 | + update: { name?: string | null; value?: string }, | |
| 735 | + ) => | |
| 736 | + stateManager.mutate((state) => { | |
| 737 | + let updated: SecretMaterial | null = null; | |
| 738 | + const nextMaterials = state.secretMaterials.map((material) => { | |
| 739 | + if (material.id !== id) { | |
| 740 | + return material; | |
| 741 | + } | |
| 742 | + | |
| 743 | + updated = { | |
| 744 | + ...material, | |
| 745 | + ...(update.name !== undefined ? { name: update.name } : {}), | |
| 746 | + ...(update.value !== undefined ? { value: update.value } : {}), | |
| 747 | + updatedAt: Date.now(), | |
| 748 | + } satisfies SecretMaterial; | |
| 749 | + return updated; | |
| 750 | + }); | |
| 751 | + | |
| 752 | + return { | |
| 753 | + state: { | |
| 754 | + ...state, | |
| 755 | + secretMaterials: nextMaterials, | |
| 756 | + }, | |
| 757 | + value: updated ? Option.some(cloneValue(updated)) : Option.none<SecretMaterial>(), | |
| 758 | + } satisfies StateMutationResult<Option.Option<SecretMaterial>>; | |
| 759 | + }), | |
| 760 | + | |
| 761 | + removeById: (id: SecretMaterial["id"]) => | |
| 762 | + stateManager.mutate((state) => { | |
| 763 | + const nextMaterials = state.secretMaterials.filter( | |
| 764 | + (candidate) => candidate.id !== id, | |
| 765 | + ); | |
| 766 | + | |
| 767 | + return { | |
| 768 | + state: { | |
| 769 | + ...state, | |
| 770 | + secretMaterials: nextMaterials, | |
| 771 | + }, | |
| 772 | + value: nextMaterials.length !== state.secretMaterials.length, | |
| 773 | + } satisfies StateMutationResult<boolean>; | |
| 774 | + }), | |
| 775 | + | |
| 776 | + listLinkedSources: () => | |
| 777 | + Effect.gen(function* () { | |
| 778 | + const [state, loadedConfig] = yield* Effect.all([ | |
| 779 | + stateManager.read((current) => current), | |
| 780 | + loadLocalExecutorConfig(context), | |
| 781 | + ]); | |
| 782 | + | |
| 783 | + const sourceNames = new Map<string, string>(); | |
| 784 | + for (const [sourceId, source] of Object.entries( | |
| 785 | + loadedConfig.config?.sources ?? {}, | |
| 786 | + )) { | |
| 787 | + sourceNames.set(sourceId, source.name ?? sourceId); | |
| 788 | + } | |
| 789 | + | |
| 790 | + const materialIds = new Set( | |
| 791 | + state.secretMaterials.map((material) => String(material.id)), | |
| 792 | + ); | |
| 793 | + const linkedSources = new Map<string, Array<{ sourceId: string; sourceName: string }>>(); | |
| 794 | + | |
| 795 | + for (const artifact of state.authArtifacts) { | |
| 796 | + for (const ref of authArtifactSecretMaterialRefs(artifact)) { | |
| 797 | + if (!materialIds.has(ref.handle)) { | |
| 798 | + continue; | |
| 799 | + } | |
| 800 | + | |
| 801 | + const existing = linkedSources.get(ref.handle) ?? []; | |
| 802 | + if (!existing.some((link) => link.sourceId === artifact.sourceId)) { | |
| 803 | + existing.push({ | |
| 804 | + sourceId: artifact.sourceId, | |
| 805 | + sourceName: sourceNames.get(artifact.sourceId) ?? artifact.sourceId, | |
| 806 | + }); | |
| 807 | + linkedSources.set(ref.handle, existing); | |
| 808 | + } | |
| 809 | + } | |
| 810 | + } | |
| 811 | + | |
| 812 | + return linkedSources; | |
| 813 | + }), | |
| 814 | + }, | |
| 815 | + | |
| 816 | + executions: { | |
| 817 | + getById: (executionId: Execution["id"]) => | |
| 818 | + stateManager.read((state) => { | |
| 819 | + const execution = state.executions.find( | |
| 820 | + (candidate) => candidate.id === executionId, | |
| 821 | + ); | |
| 822 | + return execution | |
| 823 | + ? Option.some(cloneValue(execution)) | |
| 824 | + : Option.none<Execution>(); | |
| 825 | + }), | |
| 826 | + | |
| 827 | + getByWorkspaceAndId: ( | |
| 828 | + workspaceId: Execution["workspaceId"], | |
| 829 | + executionId: Execution["id"], | |
| 830 | + ) => | |
| 831 | + stateManager.read((state) => { | |
| 832 | + const execution = state.executions.find( | |
| 833 | + (candidate) => | |
| 834 | + candidate.workspaceId === workspaceId && candidate.id === executionId, | |
| 835 | + ); | |
| 836 | + return execution | |
| 837 | + ? Option.some(cloneValue(execution)) | |
| 838 | + : Option.none<Execution>(); | |
| 839 | + }), | |
| 840 | + | |
| 841 | + insert: (execution: Execution) => | |
| 842 | + stateManager.mutate((state) => ({ | |
| 843 | + state: { | |
| 844 | + ...state, | |
| 845 | + executions: [...state.executions, cloneValue(execution)], | |
| 846 | + }, | |
| 847 | + value: undefined, | |
| 848 | + } satisfies StateMutationResult<void>)), | |
| 849 | + | |
| 850 | + update: ( | |
| 851 | + executionId: Execution["id"], | |
| 852 | + patch: Partial< | |
| 853 | + Omit<Execution, "id" | "workspaceId" | "createdByAccountId" | "createdAt"> | |
| 854 | + >, | |
| 855 | + ) => | |
| 856 | + stateManager.mutate((state) => { | |
| 857 | + let updated: Execution | null = null; | |
| 858 | + const nextExecutions = state.executions.map((execution) => { | |
| 859 | + if (execution.id !== executionId) { | |
| 860 | + return execution; | |
| 861 | + } | |
| 862 | + | |
| 863 | + updated = { | |
| 864 | + ...execution, | |
| 865 | + ...cloneValue(patch), | |
| 866 | + } satisfies Execution; | |
| 867 | + return updated; | |
| 868 | + }); | |
| 869 | + | |
| 870 | + return { | |
| 871 | + state: { | |
| 872 | + ...state, | |
| 873 | + executions: nextExecutions, | |
| 874 | + }, | |
| 875 | + value: updated ? Option.some(cloneValue(updated)) : Option.none<Execution>(), | |
| 876 | + } satisfies StateMutationResult<Option.Option<Execution>>; | |
| 877 | + }), | |
| 878 | + }, | |
| 879 | + | |
| 880 | + executionInteractions: { | |
| 881 | + getById: (interactionId: ExecutionInteraction["id"]) => | |
| 882 | + stateManager.read((state) => { | |
| 883 | + const interaction = state.executionInteractions.find( | |
| 884 | + (candidate) => candidate.id === interactionId, | |
| 885 | + ); | |
| 886 | + return interaction | |
| 887 | + ? Option.some(cloneValue(interaction)) | |
| 888 | + : Option.none<ExecutionInteraction>(); | |
| 889 | + }), | |
| 890 | + | |
| 891 | + listByExecutionId: (executionId: ExecutionInteraction["executionId"]) => | |
| 892 | + stateManager.read((state) => | |
| 893 | + sortByUpdatedAtAndIdDesc( | |
| 894 | + state.executionInteractions.filter( | |
| 895 | + (interaction) => interaction.executionId === executionId, | |
| 896 | + ), | |
| 897 | + ), | |
| 898 | + ), | |
| 899 | + | |
| 900 | + getPendingByExecutionId: (executionId: ExecutionInteraction["executionId"]) => | |
| 901 | + stateManager.read((state) => { | |
| 902 | + const interaction = sortByUpdatedAtAndIdDesc( | |
| 903 | + state.executionInteractions.filter( | |
| 904 | + (candidate) => | |
| 905 | + candidate.executionId === executionId && candidate.status === "pending", | |
| 906 | + ), | |
| 907 | + )[0] ?? null; | |
| 908 | + | |
| 909 | + return interaction | |
| 910 | + ? Option.some(cloneValue(interaction)) | |
| 911 | + : Option.none<ExecutionInteraction>(); | |
| 912 | + }), | |
| 913 | + | |
| 914 | + insert: (interaction: ExecutionInteraction) => | |
| 915 | + stateManager.mutate((state) => ({ | |
| 916 | + state: { | |
| 917 | + ...state, | |
| 918 | + executionInteractions: [ | |
| 919 | + ...state.executionInteractions, | |
| 920 | + cloneValue(interaction), | |
| 921 | + ], | |
| 922 | + }, | |
| 923 | + value: undefined, | |
| 924 | + } satisfies StateMutationResult<void>)), | |
| 925 | + | |
| 926 | + update: ( | |
| 927 | + interactionId: ExecutionInteraction["id"], | |
| 928 | + patch: Partial< | |
| 929 | + Omit< | |
| 930 | + ExecutionInteraction, | |
| 931 | + "id" | "executionId" | "createdAt" | |
| 932 | + > | |
| 933 | + >, | |
| 934 | + ) => | |
| 935 | + stateManager.mutate((state) => { | |
| 936 | + let updated: ExecutionInteraction | null = null; | |
| 937 | + const nextInteractions = state.executionInteractions.map((interaction) => { | |
| 938 | + if (interaction.id !== interactionId) { | |
| 939 | + return interaction; | |
| 940 | + } | |
| 941 | + | |
| 942 | + updated = { | |
| 943 | + ...interaction, | |
| 944 | + ...cloneValue(patch), | |
| 945 | + } as ExecutionInteraction; | |
| 946 | + return updated; | |
| 947 | + }); | |
| 948 | + | |
| 949 | + return { | |
| 950 | + state: { | |
| 951 | + ...state, | |
| 952 | + executionInteractions: nextInteractions, | |
| 953 | + }, | |
| 954 | + value: updated | |
| 955 | + ? Option.some(cloneValue(updated)) | |
| 956 | + : Option.none<ExecutionInteraction>(), | |
| 957 | + } satisfies StateMutationResult<Option.Option<ExecutionInteraction>>; | |
| 958 | + }), | |
| 959 | + }, | |
| 960 | + | |
| 961 | + executionSteps: { | |
| 962 | + getByExecutionAndSequence: ( | |
| 963 | + executionId: ExecutionStep["executionId"], | |
| 964 | + sequence: ExecutionStep["sequence"], | |
| 965 | + ) => | |
| 966 | + stateManager.read((state) => { | |
| 967 | + const step = state.executionSteps.find( | |
| 968 | + (candidate) => | |
| 969 | + candidate.executionId === executionId && candidate.sequence === sequence, | |
| 970 | + ); | |
| 971 | + return step | |
| 972 | + ? Option.some(cloneValue(step)) | |
| 973 | + : Option.none<ExecutionStep>(); | |
| 974 | + }), | |
| 975 | + | |
| 976 | + listByExecutionId: (executionId: ExecutionStep["executionId"]) => | |
| 977 | + stateManager.read((state) => | |
| 978 | + [...state.executionSteps] | |
| 979 | + .filter((step) => step.executionId === executionId) | |
| 980 | + .sort( | |
| 981 | + (left, right) => | |
| 982 | + left.sequence - right.sequence | |
| 983 | + || right.updatedAt - left.updatedAt, | |
| 984 | + ), | |
| 985 | + ), | |
| 986 | + | |
| 987 | + insert: (step: ExecutionStep) => | |
| 988 | + stateManager.mutate((state) => ({ | |
| 989 | + state: { | |
| 990 | + ...state, | |
| 991 | + executionSteps: [...state.executionSteps, cloneValue(step)], | |
| 992 | + }, | |
| 993 | + value: undefined, | |
| 994 | + } satisfies StateMutationResult<void>)), | |
| 995 | + | |
| 996 | + deleteByExecutionId: (executionId: ExecutionStep["executionId"]) => | |
| 997 | + stateManager.mutate((state) => ({ | |
| 998 | + state: { | |
| 999 | + ...state, | |
| 1000 | + executionSteps: state.executionSteps.filter( | |
| 1001 | + (step) => step.executionId !== executionId, | |
| 1002 | + ), | |
| 1003 | + }, | |
| 1004 | + value: undefined, | |
| 1005 | + } satisfies StateMutationResult<void>)), | |
| 1006 | + | |
| 1007 | + updateByExecutionAndSequence: ( | |
| 1008 | + executionId: ExecutionStep["executionId"], | |
| 1009 | + sequence: ExecutionStep["sequence"], | |
| 1010 | + patch: Partial< | |
| 1011 | + Omit< | |
| 1012 | + ExecutionStep, | |
| 1013 | + "id" | "executionId" | "sequence" | "createdAt" | |
| 1014 | + > | |
| 1015 | + >, | |
| 1016 | + ) => | |
| 1017 | + stateManager.mutate((state) => { | |
| 1018 | + let updated: ExecutionStep | null = null; | |
| 1019 | + const nextSteps = state.executionSteps.map((step) => { | |
| 1020 | + if (step.executionId !== executionId || step.sequence !== sequence) { | |
| 1021 | + return step; | |
| 1022 | + } | |
| 1023 | + | |
| 1024 | + updated = { | |
| 1025 | + ...step, | |
| 1026 | + ...cloneValue(patch), | |
| 1027 | + } as ExecutionStep; | |
| 1028 | + return updated; | |
| 1029 | + }); | |
| 1030 | + | |
| 1031 | + return { | |
| 1032 | + state: { | |
| 1033 | + ...state, | |
| 1034 | + executionSteps: nextSteps, | |
| 1035 | + }, | |
| 1036 | + value: updated | |
| 1037 | + ? Option.some(cloneValue(updated)) | |
| 1038 | + : Option.none<ExecutionStep>(), | |
| 1039 | + } satisfies StateMutationResult<Option.Option<ExecutionStep>>; | |
| 1040 | + }), | |
| 1041 | + }, | |
| 1042 | + }; | |
| 1043 | +}; | |
| 1044 | + | |
| 1045 | +export type LocalControlPlaneStore = ReturnType<typeof createLocalControlPlaneStore>; | |
| 1046 | + | |
| 1047 | +export const createLocalControlPlanePersistence = ( | |
| 1048 | + context: ResolvedLocalWorkspaceContext, | |
| 1049 | +): LocalControlPlanePersistence => ({ | |
| 1050 | + rows: createLocalControlPlaneStore(context), | |
| 1051 | + close: async () => {}, | |
| 1052 | +}); | |
| 1053 | + | |
| 1054 | +export { localControlPlaneStatePath }; | |
| @@ -1,4 +1,3 @@ | ||
| 1 | −import { ControlPlanePersistenceError } from "#persistence"; | |
| 2 | 1 | import type { |
| 3 | 2 | Execution, |
| 4 | 3 | ExecutionInteraction, |
| @@ -12,13 +11,13 @@ import { · 7 unmodified lines | ||
| 12 | 11 | import * as Effect from "effect/Effect"; |
| 13 | 12 | import * as Option from "effect/Option"; |
| 14 | 13 | |
| 15 | −import { loadLocalInstallation } from "./local-installation"; | |
| 16 | 14 | import { submitExecutionInteractionResponse } from "./execution-service"; |
| 17 | 15 | import { LiveExecutionManagerService } from "./live-execution"; |
| 18 | 16 | import { |
| 19 | 17 | operationErrors, |
| 20 | 18 | } from "./operation-errors"; |
| 21 | 19 | import { resolveLocalWorkspaceContext } from "./local-config"; |
| 20 | +import { InstallationStore, LocalInstallationStore } from "./local-storage"; | |
| 22 | 21 | import { getRuntimeLocalWorkspaceOption } from "./local-runtime-context"; |
| 23 | 22 | import { RuntimeSourceAuthServiceTag } from "./source-auth-service"; |
| 24 | 23 | import { |
| @@ -130,12 +129,10 @@ const loadSourceCredentialInteraction = (input: { · 105 unmodified lines | ||
| 130 | 129 | |
| 131 | 130 | const stored = yield* store.executionInteractions.getById(input.interactionId).pipe( |
| 132 | 131 | Effect.mapError((error) => |
| 133 | − error instanceof ControlPlanePersistenceError | |
| 134 | − ? input.operation.storage(error) | |
| 135 | − : input.operation.unknownStorage( | |
| 136 | − error, | |
| 137 | − `Failed loading execution interaction ${input.interactionId}`, | |
| 138 | − ), | |
| 132 | + input.operation.unknownStorage( | |
| 133 | + error, | |
| 134 | + `Failed loading execution interaction ${input.interactionId}`, | |
| 135 | + ), | |
| 139 | 136 | ), |
| 140 | 137 | ); |
| 141 | 138 | |
| @@ -194,7 +191,13 @@ export const getLocalInstallation = () => · 52 unmodified lines | ||
| 194 | 191 | ), |
| 195 | 192 | )); |
| 196 | 193 | |
| 197 | − return yield* loadLocalInstallation(context); | |
| 194 | + const installationStore = runtimeLocalWorkspace !== null | |
| 195 | + ? yield* InstallationStore | |
| 196 | + : LocalInstallationStore; | |
| 197 | + | |
| 198 | + return yield* installationStore.load( | |
| 199 | + context, | |
| 200 | + ); | |
| 198 | 201 | }); |
| 199 | 202 | |
| 200 | 203 | export const getSourceCredentialInteraction = (input: { |
| @@ -331,12 +334,10 @@ export const submitSourceCredentialInteraction = (input: { · 130 unmodified lines | ||
| 331 | 334 | value: token, |
| 332 | 335 | }).pipe( |
| 333 | 336 | Effect.mapError((error) => |
| 334 | − error instanceof ControlPlanePersistenceError | |
| 335 | − ? localOps.sourceCredentialSubmit.storage(error) | |
| 336 | − : localOps.sourceCredentialSubmit.unknownStorage( | |
| 337 | − error, | |
| 338 | − `Failed storing credential material for interaction ${interaction.interactionId}`, | |
| 339 | − ), | |
| 337 | + localOps.sourceCredentialSubmit.unknownStorage( | |
| 338 | + error, | |
| 339 | + `Failed storing credential material for interaction ${interaction.interactionId}`, | |
| 340 | + ), | |
| 340 | 341 | ), |
| 341 | 342 | ); |
| 342 | 343 | const resumed = yield* liveExecutionManager.resolveInteraction({ |
| @@ -0,0 +1,154 @@ | ||
| 1 | +import type { LocalInstallation, LocalExecutorConfig } from "#schema"; | |
| 2 | +import * as Context from "effect/Context"; | |
| 3 | +import type * as Effect from "effect/Effect"; | |
| 4 | +import * as Layer from "effect/Layer"; | |
| 5 | + | |
| 6 | +import type { | |
| 7 | + LoadedLocalExecutorConfig, | |
| 8 | + ResolvedLocalWorkspaceContext, | |
| 9 | +} from "./local-config"; | |
| 10 | +import { | |
| 11 | + loadLocalExecutorConfig, | |
| 12 | + resolveConfigRelativePath, | |
| 13 | + writeProjectLocalExecutorConfig, | |
| 14 | +} from "./local-config"; | |
| 15 | +import { | |
| 16 | + getOrProvisionLocalInstallation, | |
| 17 | + loadLocalInstallation, | |
| 18 | +} from "./local-installation"; | |
| 19 | +import type { | |
| 20 | + LocalSourceArtifact, | |
| 21 | +} from "./local-source-artifacts"; | |
| 22 | +import { | |
| 23 | + buildLocalSourceArtifact, | |
| 24 | + readLocalSourceArtifact, | |
| 25 | + removeLocalSourceArtifact, | |
| 26 | + writeLocalSourceArtifact, | |
| 27 | +} from "./local-source-artifacts"; | |
| 28 | +import type { LocalWorkspaceState } from "./local-workspace-state"; | |
| 29 | +import { | |
| 30 | + loadLocalWorkspaceState, | |
| 31 | + writeLocalWorkspaceState, | |
| 32 | +} from "./local-workspace-state"; | |
| 33 | +import type { SourceRecipeMaterialization } from "./source-recipe-support"; | |
| 34 | +import type { Source } from "#schema"; | |
| 35 | + | |
| 36 | +export type InstallationStoreShape = { | |
| 37 | + load: ( | |
| 38 | + context: ResolvedLocalWorkspaceContext, | |
| 39 | + ) => Effect.Effect<LocalInstallation, never, never>; | |
| 40 | + getOrProvision: (input: { | |
| 41 | + context: ResolvedLocalWorkspaceContext; | |
| 42 | + }) => Effect.Effect<LocalInstallation, never, never>; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +export class InstallationStore extends Context.Tag( | |
| 46 | + "#runtime/InstallationStore", | |
| 47 | +)<InstallationStore, InstallationStoreShape>() {} | |
| 48 | + | |
| 49 | +export type WorkspaceConfigStoreShape = { | |
| 50 | + load: ( | |
| 51 | + context: ResolvedLocalWorkspaceContext, | |
| 52 | + ) => Effect.Effect<LoadedLocalExecutorConfig, Error, never>; | |
| 53 | + writeProject: (input: { | |
| 54 | + context: ResolvedLocalWorkspaceContext; | |
| 55 | + config: LocalExecutorConfig; | |
| 56 | + }) => Effect.Effect<void, Error, never>; | |
| 57 | + resolveRelativePath: (input: { path: string; workspaceRoot: string }) => string; | |
| 58 | +}; | |
| 59 | + | |
| 60 | +export class WorkspaceConfigStore extends Context.Tag( | |
| 61 | + "#runtime/WorkspaceConfigStore", | |
| 62 | +)<WorkspaceConfigStore, WorkspaceConfigStoreShape>() {} | |
| 63 | + | |
| 64 | +export type WorkspaceStateStoreShape = { | |
| 65 | + load: ( | |
| 66 | + context: ResolvedLocalWorkspaceContext, | |
| 67 | + ) => Effect.Effect<LocalWorkspaceState, Error, never>; | |
| 68 | + write: (input: { | |
| 69 | + context: ResolvedLocalWorkspaceContext; | |
| 70 | + state: LocalWorkspaceState; | |
| 71 | + }) => Effect.Effect<void, Error, never>; | |
| 72 | +}; | |
| 73 | + | |
| 74 | +export class WorkspaceStateStore extends Context.Tag( | |
| 75 | + "#runtime/WorkspaceStateStore", | |
| 76 | +)<WorkspaceStateStore, WorkspaceStateStoreShape>() {} | |
| 77 | + | |
| 78 | +export type SourceArtifactStoreShape = { | |
| 79 | + build: (input: { | |
| 80 | + source: Source; | |
| 81 | + materialization: SourceRecipeMaterialization; | |
| 82 | + }) => LocalSourceArtifact; | |
| 83 | + read: (input: { | |
| 84 | + context: ResolvedLocalWorkspaceContext; | |
| 85 | + sourceId: string; | |
| 86 | + }) => Effect.Effect<LocalSourceArtifact | null, Error, never>; | |
| 87 | + write: (input: { | |
| 88 | + context: ResolvedLocalWorkspaceContext; | |
| 89 | + sourceId: string; | |
| 90 | + artifact: LocalSourceArtifact; | |
| 91 | + }) => Effect.Effect<void, Error, never>; | |
| 92 | + remove: (input: { | |
| 93 | + context: ResolvedLocalWorkspaceContext; | |
| 94 | + sourceId: string; | |
| 95 | + }) => Effect.Effect<void, Error, never>; | |
| 96 | +}; | |
| 97 | + | |
| 98 | +export class SourceArtifactStore extends Context.Tag( | |
| 99 | + "#runtime/SourceArtifactStore", | |
| 100 | +)<SourceArtifactStore, SourceArtifactStoreShape>() {} | |
| 101 | + | |
| 102 | +export type LocalStorageServices = | |
| 103 | + | InstallationStore | |
| 104 | + | WorkspaceConfigStore | |
| 105 | + | WorkspaceStateStore | |
| 106 | + | SourceArtifactStore; | |
| 107 | + | |
| 108 | +export type WorkspaceStorageServices = | |
| 109 | + | WorkspaceConfigStore | |
| 110 | + | WorkspaceStateStore | |
| 111 | + | SourceArtifactStore; | |
| 112 | + | |
| 113 | +export const LocalInstallationStore: InstallationStoreShape = { | |
| 114 | + load: loadLocalInstallation, | |
| 115 | + getOrProvision: getOrProvisionLocalInstallation, | |
| 116 | +}; | |
| 117 | + | |
| 118 | +export const LocalInstallationStoreLive = Layer.succeed( | |
| 119 | + InstallationStore, | |
| 120 | + LocalInstallationStore, | |
| 121 | +); | |
| 122 | + | |
| 123 | +export const LocalWorkspaceConfigStore: WorkspaceConfigStoreShape = { | |
| 124 | + load: loadLocalExecutorConfig, | |
| 125 | + writeProject: writeProjectLocalExecutorConfig, | |
| 126 | + resolveRelativePath: resolveConfigRelativePath, | |
| 127 | +}; | |
| 128 | + | |
| 129 | +export const LocalWorkspaceConfigStoreLive = Layer.succeed( | |
| 130 | + WorkspaceConfigStore, | |
| 131 | + LocalWorkspaceConfigStore, | |
| 132 | +); | |
| 133 | + | |
| 134 | +export const LocalWorkspaceStateStore: WorkspaceStateStoreShape = { | |
| 135 | + load: loadLocalWorkspaceState, | |
| 136 | + write: writeLocalWorkspaceState, | |
| 137 | +}; | |
| 138 | + | |
| 139 | +export const LocalWorkspaceStateStoreLive = Layer.succeed( | |
| 140 | + WorkspaceStateStore, | |
| 141 | + LocalWorkspaceStateStore, | |
| 142 | +); | |
| 143 | + | |
| 144 | +export const LocalSourceArtifactStore: SourceArtifactStoreShape = { | |
| 145 | + build: buildLocalSourceArtifact, | |
| 146 | + read: readLocalSourceArtifact, | |
| 147 | + write: writeLocalSourceArtifact, | |
| 148 | + remove: removeLocalSourceArtifact, | |
| 149 | +}; | |
| 150 | + | |
| 151 | +export const LocalSourceArtifactStoreLive = Layer.succeed( | |
| 152 | + SourceArtifactStore, | |
| 153 | + LocalSourceArtifactStore, | |
| 154 | +); | |
| @@ -540,7 +540,7 @@ const importLocalToolModule = (input: { · 539 unmodified lines | ||
| 540 | 540 | toolPath: ToolPath; |
| 541 | 541 | }): Effect.Effect<ToolInput, LocalToolImportError | LocalToolDefinitionError> => |
| 542 | 542 | Effect.tryPromise({ |
| 543 | − try: () => import(pathToFileURL(resolve(input.artifactPath)).href), | |
| 543 | + try: () => import(/* @vite-ignore */ pathToFileURL(resolve(input.artifactPath)).href), | |
| 544 | 544 | catch: (cause) => |
| 545 | 545 | new LocalToolImportError({ |
| 546 | 546 | message: `Failed to import local tool ${input.sourcePath}`, |
| @@ -3,25 +3,8 @@ import { · 2 unmodified lines | ||
| 3 | 3 | ControlPlaneNotFoundError, |
| 4 | 4 | ControlPlaneStorageError, |
| 5 | 5 | } from "../api/errors"; |
| 6 | −import { ControlPlanePersistenceError } from "#persistence"; | |
| 7 | 6 | import * as Effect from "effect/Effect"; |
| 8 | 7 | |
| 9 | −const unknownPersistenceError = ( | |
| 10 | − operation: string, | |
| 11 | − cause: unknown, | |
| 12 | − details: string, | |
| 13 | −): ControlPlanePersistenceError => | |
| 14 | − new ControlPlanePersistenceError({ | |
| 15 | − operation, | |
| 16 | − message: cause instanceof Error ? cause.message : String(cause), | |
| 17 | − details, | |
| 18 | − code: null, | |
| 19 | − constraint: null, | |
| 20 | − table: null, | |
| 21 | − kind: "unknown", | |
| 22 | − cause, | |
| 23 | − }); | |
| 24 | − | |
| 25 | 8 | export type OperationErrors<TOperation extends string = string> = { |
| 26 | 9 | readonly operation: TOperation; |
| 27 | 10 | readonly child: <TSuffix extends string>( |
| @@ -36,18 +19,14 @@ export type OperationErrors<TOperation extends string = string> = { · 8 unmodified lines | ||
| 36 | 19 | details: string, |
| 37 | 20 | ) => ControlPlaneNotFoundError; |
| 38 | 21 | readonly storage: ( |
| 39 | − error: ControlPlanePersistenceError, | |
| 22 | + error: Error, | |
| 40 | 23 | ) => ControlPlaneStorageError; |
| 41 | − readonly unknownPersistence: ( | |
| 42 | − cause: unknown, | |
| 43 | − details: string, | |
| 44 | − ) => ControlPlanePersistenceError; | |
| 45 | 24 | readonly unknownStorage: ( |
| 46 | 25 | cause: unknown, |
| 47 | 26 | details: string, |
| 48 | 27 | ) => ControlPlaneStorageError; |
| 49 | − readonly mapStorage: <A>( | |
| 50 | − effect: Effect.Effect<A, ControlPlanePersistenceError>, | |
| 28 | + readonly mapStorage: <A, E extends Error>( | |
| 29 | + effect: Effect.Effect<A, E>, | |
| 51 | 30 | ) => Effect.Effect<A, ControlPlaneStorageError>; |
| 52 | 31 | }; |
| 53 | 32 | |
| @@ -76,15 +55,17 @@ export const operationErrors = <TOperation extends string>( · 22 unmodified lines | ||
| 76 | 55 | new ControlPlaneStorageError({ |
| 77 | 56 | operation, |
| 78 | 57 | message: error.message, |
| 79 | − details: error.details ?? "Persistence operation failed", | |
| 58 | + details: error.message, | |
| 80 | 59 | }), |
| 81 | − unknownPersistence: (cause, details) => | |
| 82 | − unknownPersistenceError(operation, cause, details), | |
| 83 | 60 | unknownStorage: (cause, details) => |
| 84 | − self.storage(self.unknownPersistence(cause, details)), | |
| 61 | + self.storage( | |
| 62 | + cause instanceof Error | |
| 63 | + ? new Error(`${cause.message}: ${details}`) | |
| 64 | + : new Error(details), | |
| 65 | + ), | |
| 85 | 66 | mapStorage: (effect) => |
| 86 | 67 | effect.pipe( |
| 87 | − Effect.mapError(self.storage), | |
| 68 | + Effect.mapError((error) => self.storage(error)), | |
| 88 | 69 | ), |
| 89 | 70 | }; |
| 90 | 71 | |
| @@ -2,7 +2,6 @@ import { · 1 unmodified line | ||
| 2 | 2 | ControlPlaneBadRequestError, |
| 3 | 3 | ControlPlaneStorageError, |
| 4 | 4 | } from "../api/errors"; |
| 5 | −import { ControlPlanePersistenceError } from "#persistence"; | |
| 6 | 5 | import * as Effect from "effect/Effect"; |
| 7 | 6 | |
| 8 | 7 | import { |
| @@ -11,26 +10,14 @@ import { · 2 unmodified lines | ||
| 11 | 10 | } from "./operation-errors"; |
| 12 | 11 | export type Mutable<T> = { -readonly [K in keyof T]: T[K] }; |
| 13 | 12 | |
| 14 | −const isUniqueViolation = (error: ControlPlanePersistenceError): boolean => | |
| 15 | − error.kind === "unique_violation"; | |
| 16 | − | |
| 17 | −export const mapPersistenceError = <A>( | |
| 13 | +export const mapPersistenceError = <A, R>( | |
| 18 | 14 | operation: OperationErrorsLike, |
| 19 | − effect: Effect.Effect<A, ControlPlanePersistenceError | Error>, | |
| 20 | −): Effect.Effect<A, ControlPlaneBadRequestError | ControlPlaneStorageError> => | |
| 15 | + effect: Effect.Effect<A, Error, R>, | |
| 16 | +): Effect.Effect<A, ControlPlaneBadRequestError | ControlPlaneStorageError, R> => | |
| 21 | 17 | effect.pipe( |
| 22 | 18 | Effect.mapError((error) => { |
| 23 | 19 | const errors = asOperationErrors(operation); |
| 24 | − if (error instanceof ControlPlanePersistenceError) { | |
| 25 | − return isUniqueViolation(error) | |
| 26 | − ? errors.badRequest("Unique constraint violation", error.details ?? "duplicate key") | |
| 27 | − : errors.storage(error); | |
| 28 | − } | |
| 29 | − | |
| 30 | − return errors.unknownStorage( | |
| 31 | − error, | |
| 32 | − error.message, | |
| 33 | − ); | |
| 20 | + return errors.storage(error); | |
| 34 | 21 | }), |
| 35 | 22 | ); |
| 36 | 23 | |
| @@ -13,17 +13,21 @@ import { · 12 unmodified lines | ||
| 13 | 13 | } from "#schema"; |
| 14 | 14 | import * as Effect from "effect/Effect"; |
| 15 | 15 | |
| 16 | −import { | |
| 17 | − loadLocalExecutorConfig, | |
| 18 | − writeProjectLocalExecutorConfig, | |
| 19 | −} from "./local-config"; | |
| 20 | 16 | import { |
| 21 | 17 | requireRuntimeLocalWorkspace, |
| 22 | 18 | } from "./local-runtime-context"; |
| 19 | +import type { | |
| 20 | + WorkspaceStorageServices, | |
| 21 | + WorkspaceConfigStoreShape, | |
| 22 | + WorkspaceStateStoreShape, | |
| 23 | +} from "./local-storage"; | |
| 24 | +import { | |
| 25 | + WorkspaceConfigStore, | |
| 26 | + WorkspaceStateStore, | |
| 27 | +} from "./local-storage"; | |
| 28 | +import type { ResolvedLocalWorkspaceContext } from "./local-config"; | |
| 23 | 29 | import { |
| 24 | − loadLocalWorkspaceState, | |
| 25 | 30 | type LocalWorkspaceState, |
| 26 | − writeLocalWorkspaceState, | |
| 27 | 31 | } from "./local-workspace-state"; |
| 28 | 32 | import { |
| 29 | 33 | derivePolicyConfigKey, |
| @@ -77,8 +81,14 @@ const toLocalWorkspacePolicy = (input: { · 47 unmodified lines | ||
| 77 | 81 | export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => |
| 78 | 82 | Effect.gen(function* () { |
| 79 | 83 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 80 | − const loadedConfig = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 81 | − const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 84 | + const workspaceConfigStore = yield* WorkspaceConfigStore; | |
| 85 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 86 | + const loadedConfig = yield* workspaceConfigStore.load( | |
| 87 | + runtimeLocalWorkspace.context, | |
| 88 | + ); | |
| 89 | + const workspaceState = yield* workspaceStateStore.load( | |
| 90 | + runtimeLocalWorkspace.context, | |
| 91 | + ); | |
| 82 | 92 | |
| 83 | 93 | const policies = Object.entries(loadedConfig.config?.policies ?? {}).map(([key, policyConfig]) => |
| 84 | 94 | toLocalWorkspacePolicy({ |
| @@ -99,16 +109,18 @@ export const loadRuntimeLocalWorkspacePolicies = (workspaceId: WorkspaceId) => · 14 unmodified lines | ||
| 99 | 109 | |
| 100 | 110 | const writeLocalPolicyFiles = (input: { |
| 101 | 111 | operation: OperationErrors; |
| 102 | − context: Parameters<typeof writeProjectLocalExecutorConfig>[0]["context"]; | |
| 112 | + context: ResolvedLocalWorkspaceContext; | |
| 113 | + workspaceConfigStore: WorkspaceConfigStoreShape; | |
| 114 | + workspaceStateStore: WorkspaceStateStoreShape; | |
| 103 | 115 | projectConfig: LocalExecutorConfig; |
| 104 | 116 | workspaceState: LocalWorkspaceState; |
| 105 | 117 | }) => |
| 106 | 118 | Effect.all([ |
| 107 | − writeProjectLocalExecutorConfig({ | |
| 119 | + input.workspaceConfigStore.writeProject({ | |
| 108 | 120 | context: input.context, |
| 109 | 121 | config: input.projectConfig, |
| 110 | 122 | }), |
| 111 | − writeLocalWorkspaceState({ | |
| 123 | + input.workspaceStateStore.write({ | |
| 112 | 124 | context: input.context, |
| 113 | 125 | state: input.workspaceState, |
| 114 | 126 | }), |
| @@ -154,6 +166,8 @@ export const createPolicy = (input: { · 39 unmodified lines | ||
| 154 | 166 | }) => |
| 155 | 167 | Effect.gen(function* () { |
| 156 | 168 | const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(policyOps.create, input.workspaceId); |
| 169 | + const workspaceConfigStore = yield* WorkspaceConfigStore; | |
| 170 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 157 | 171 | const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(input.workspaceId).pipe( |
| 158 | 172 | Effect.mapError((cause) => |
| 159 | 173 | policyOps.create.unknownStorage( |
| @@ -201,6 +215,8 @@ export const createPolicy = (input: { · 41 unmodified lines | ||
| 201 | 215 | yield* writeLocalPolicyFiles({ |
| 202 | 216 | operation: policyOps.create, |
| 203 | 217 | context: runtimeLocalWorkspace.context, |
| 218 | + workspaceConfigStore, | |
| 219 | + workspaceStateStore, | |
| 204 | 220 | projectConfig: { |
| 205 | 221 | ...projectConfig, |
| 206 | 222 | policies, |
| @@ -250,6 +266,8 @@ export const updatePolicy = (input: { · 43 unmodified lines | ||
| 250 | 266 | }) => |
| 251 | 267 | Effect.gen(function* () { |
| 252 | 268 | const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(policyOps.update, input.workspaceId); |
| 269 | + const workspaceConfigStore = yield* WorkspaceConfigStore; | |
| 270 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 253 | 271 | const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(input.workspaceId).pipe( |
| 254 | 272 | Effect.mapError((cause) => |
| 255 | 273 | policyOps.update.unknownStorage( |
| @@ -300,6 +318,8 @@ export const updatePolicy = (input: { · 44 unmodified lines | ||
| 300 | 318 | yield* writeLocalPolicyFiles({ |
| 301 | 319 | operation: policyOps.update, |
| 302 | 320 | context: runtimeLocalWorkspace.context, |
| 321 | + workspaceConfigStore, | |
| 322 | + workspaceStateStore, | |
| 303 | 323 | projectConfig: { |
| 304 | 324 | ...projectConfig, |
| 305 | 325 | policies, |
| @@ -322,6 +342,8 @@ export const removePolicy = (input: { · 16 unmodified lines | ||
| 322 | 342 | }) => |
| 323 | 343 | Effect.gen(function* () { |
| 324 | 344 | const runtimeLocalWorkspace = yield* loadWorkspacePolicyContext(policyOps.remove, input.workspaceId); |
| 345 | + const workspaceConfigStore = yield* WorkspaceConfigStore; | |
| 346 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 325 | 347 | const localWorkspace = yield* loadRuntimeLocalWorkspacePolicies(input.workspaceId).pipe( |
| 326 | 348 | Effect.mapError((cause) => |
| 327 | 349 | policyOps.remove.unknownStorage( |
| @@ -343,6 +365,8 @@ export const removePolicy = (input: { · 15 unmodified lines | ||
| 343 | 365 | yield* writeLocalPolicyFiles({ |
| 344 | 366 | operation: policyOps.remove, |
| 345 | 367 | context: runtimeLocalWorkspace.context, |
| 368 | + workspaceConfigStore, | |
| 369 | + workspaceStateStore, | |
| 346 | 370 | projectConfig: { |
| 347 | 371 | ...projectConfig, |
| 348 | 372 | policies, |
| @@ -4,7 +4,6 @@ import { promises as fs } from "node:fs"; · 3 unmodified lines | ||
| 4 | 4 | import { lstatSync, realpathSync } from "node:fs"; |
| 5 | 5 | import { isAbsolute } from "node:path"; |
| 6 | 6 | |
| 7 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 8 | 7 | import { |
| 9 | 8 | type LocalConfigSecretProvider, |
| 10 | 9 | type LocalExecutorConfig, |
| @@ -17,15 +16,16 @@ import * as Option from "effect/Option"; · 6 unmodified lines | ||
| 17 | 16 | |
| 18 | 17 | import { resolveConfigRelativePath } from "./local-config"; |
| 19 | 18 | import { fromConfigSecretProviderId } from "./local-config-secrets"; |
| 19 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 20 | 20 | |
| 21 | 21 | export const ENV_SECRET_PROVIDER_ID = "env"; |
| 22 | 22 | export const PARAMS_SECRET_PROVIDER_ID = "params"; |
| 23 | 23 | export const KEYCHAIN_SECRET_PROVIDER_ID = "keychain"; |
| 24 | −export const POSTGRES_SECRET_PROVIDER_ID = "postgres"; | |
| 24 | +export const LOCAL_SECRET_PROVIDER_ID = "local"; | |
| 25 | 25 | |
| 26 | 26 | export type SecretStoreProviderId = |
| 27 | 27 | | typeof KEYCHAIN_SECRET_PROVIDER_ID |
| 28 | − | typeof POSTGRES_SECRET_PROVIDER_ID; | |
| 28 | + | typeof LOCAL_SECRET_PROVIDER_ID; | |
| 29 | 29 | |
| 30 | 30 | export type SecretMaterialResolveContext = { |
| 31 | 31 | params?: Readonly<Record<string, string | undefined>>; |
| @@ -47,7 +47,7 @@ export type DeleteSecretMaterial = ( · 15 unmodified lines | ||
| 47 | 47 | ) => Effect.Effect<boolean, Error, never>; |
| 48 | 48 | |
| 49 | 49 | type SecretMaterialProviderRuntime = { |
| 50 | − rows: SqlControlPlaneRows; | |
| 50 | + rows: ControlPlaneStoreShape; | |
| 51 | 51 | env: NodeJS.ProcessEnv; |
| 52 | 52 | dangerouslyAllowEnvSecrets: boolean; |
| 53 | 53 | keychainServiceName: string; |
| @@ -106,8 +106,10 @@ export const parseSecretStoreProviderId = (value: string | undefined): SecretSto · 52 unmodified lines | ||
| 106 | 106 | return KEYCHAIN_SECRET_PROVIDER_ID; |
| 107 | 107 | } |
| 108 | 108 | |
| 109 | − if (normalized === POSTGRES_SECRET_PROVIDER_ID) { | |
| 110 | − return POSTGRES_SECRET_PROVIDER_ID; | |
| 109 | + if ( | |
| 110 | + normalized === LOCAL_SECRET_PROVIDER_ID | |
| 111 | + ) { | |
| 112 | + return LOCAL_SECRET_PROVIDER_ID; | |
| 111 | 113 | } |
| 112 | 114 | |
| 113 | 115 | return null; |
| @@ -119,7 +121,7 @@ const resolveDangerouslyAllowEnvSecrets = (value: boolean | undefined): boolean · 5 unmodified lines | ||
| 119 | 121 | const resolveSecretStoreProviderId = (value: SecretStoreProviderId | undefined): SecretStoreProviderId => |
| 120 | 122 | value |
| 121 | 123 | ?? parseSecretStoreProviderId(process.env[SECRET_STORE_PROVIDER_ENV]) |
| 122 | − ?? POSTGRES_SECRET_PROVIDER_ID; | |
| 124 | + ?? LOCAL_SECRET_PROVIDER_ID; | |
| 123 | 125 | |
| 124 | 126 | const resolveKeychainServiceName = (value: string | undefined): string => |
| 125 | 127 | trimOrNull(value) |
| @@ -239,7 +241,7 @@ const createEnvSecretMaterialProvider = (): SecretMaterialProvider => ({ · 113 unmodified lines | ||
| 239 | 241 | remove: () => Effect.succeed(false), |
| 240 | 242 | }); |
| 241 | 243 | |
| 242 | −const createPostgresSecretMaterialProvider = (): SecretMaterialProvider => ({ | |
| 244 | +const createLocalSecretMaterialProvider = (): SecretMaterialProvider => ({ | |
| 243 | 245 | resolve: ({ ref, runtime }) => |
| 244 | 246 | Effect.gen(function* () { |
| 245 | 247 | const materialId = SecretMaterialIdSchema.make(ref.handle); |
| @@ -265,7 +267,7 @@ const createPostgresSecretMaterialProvider = (): SecretMaterialProvider => ({ · 19 unmodified lines | ||
| 265 | 267 | }); |
| 266 | 268 | |
| 267 | 269 | return { |
| 268 | − providerId: POSTGRES_SECRET_PROVIDER_ID, | |
| 270 | + providerId: LOCAL_SECRET_PROVIDER_ID, | |
| 269 | 271 | handle: id, |
| 270 | 272 | } satisfies SecretRef; |
| 271 | 273 | }), |
| @@ -467,7 +469,7 @@ const createSecretMaterialProviderRegistry = (): SecretMaterialProviderRegistry · 195 unmodified lines | ||
| 467 | 469 | [PARAMS_SECRET_PROVIDER_ID, createParamsSecretMaterialProvider()], |
| 468 | 470 | [ENV_SECRET_PROVIDER_ID, createEnvSecretMaterialProvider()], |
| 469 | 471 | [KEYCHAIN_SECRET_PROVIDER_ID, createKeychainSecretMaterialProvider()], |
| 470 | − [POSTGRES_SECRET_PROVIDER_ID, createPostgresSecretMaterialProvider()], | |
| 472 | + [LOCAL_SECRET_PROVIDER_ID, createLocalSecretMaterialProvider()], | |
| 471 | 473 | ]); |
| 472 | 474 | |
| 473 | 475 | const getSecretMaterialProvider = (input: { |
| @@ -483,7 +485,7 @@ const getSecretMaterialProvider = (input: { · 9 unmodified lines | ||
| 483 | 485 | }; |
| 484 | 486 | |
| 485 | 487 | const createSecretMaterialProviderRuntime = (input: { |
| 486 | − rows: SqlControlPlaneRows; | |
| 488 | + rows: ControlPlaneStoreShape; | |
| 487 | 489 | dangerouslyAllowEnvSecrets?: boolean; |
| 488 | 490 | keychainServiceName?: string; |
| 489 | 491 | localConfig?: LocalExecutorConfig | null; |
| @@ -644,7 +646,7 @@ const resolveConfiguredSecretProvider = (input: { · 154 unmodified lines | ||
| 644 | 646 | }; |
| 645 | 647 | |
| 646 | 648 | export const createDefaultSecretMaterialResolver = (input: { |
| 647 | − rows: SqlControlPlaneRows; | |
| 649 | + rows: ControlPlaneStoreShape; | |
| 648 | 650 | dangerouslyAllowEnvSecrets?: boolean; |
| 649 | 651 | keychainServiceName?: string; |
| 650 | 652 | localConfig?: LocalExecutorConfig | null; |
| @@ -682,7 +684,7 @@ export const createDefaultSecretMaterialResolver = (input: { · 31 unmodified lines | ||
| 682 | 684 | }; |
| 683 | 685 | |
| 684 | 686 | export const createDefaultSecretMaterialStorer = (input: { |
| 685 | − rows: SqlControlPlaneRows; | |
| 687 | + rows: ControlPlaneStoreShape; | |
| 686 | 688 | storeProviderId?: SecretStoreProviderId; |
| 687 | 689 | dangerouslyAllowEnvSecrets?: boolean; |
| 688 | 690 | keychainServiceName?: string; |
| @@ -714,7 +716,7 @@ export const createDefaultSecretMaterialStorer = (input: { · 25 unmodified lines | ||
| 714 | 716 | }; |
| 715 | 717 | |
| 716 | 718 | export const createDefaultSecretMaterialDeleter = (input: { |
| 717 | − rows: SqlControlPlaneRows; | |
| 719 | + rows: ControlPlaneStoreShape; | |
| 718 | 720 | dangerouslyAllowEnvSecrets?: boolean; |
| 719 | 721 | keychainServiceName?: string; |
| 720 | 722 | }): DeleteSecretMaterial => { |
| @@ -5,7 +5,6 @@ import { · 4 unmodified lines | ||
| 5 | 5 | makeToolInvokerFromTools, |
| 6 | 6 | typeSignatureFromSchemaJson, |
| 7 | 7 | } from "@executor/codemode-core"; |
| 8 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 9 | 8 | import type { |
| 10 | 9 | GraphqlToolManifest, |
| 11 | 10 | } from "../graphql-tools"; |
| @@ -41,7 +40,6 @@ import { · 29 unmodified lines | ||
| 41 | 40 | import type { |
| 42 | 41 | SourceAdapter, |
| 43 | 42 | SourceAdapterMaterialization, |
| 44 | − SourceAdapterRepairRevisionInput, | |
| 45 | 43 | } from "./types"; |
| 46 | 44 | import { |
| 47 | 45 | ConnectHttpAuthSchema, |
| @@ -285,75 +283,6 @@ const primaryDocument = ( · 237 unmodified lines | ||
| 285 | 283 | documents.find((document) => document.documentKind === "graphql_introspection") |
| 286 | 284 | ?? null; |
| 287 | 285 | |
| 288 | −export const rebuildGraphqlRecipeRevisionContent = (input: { | |
| 289 | − rows: SqlControlPlaneRows; | |
| 290 | − sourceRecord: SourceAdapterRepairRevisionInput["sourceRecord"]; | |
| 291 | − revision: SourceAdapterRepairRevisionInput["revision"]; | |
| 292 | − documents: readonly StoredSourceRecipeDocumentRecord[]; | |
| 293 | −}): Effect.Effect<void, Error, never> => | |
| 294 | − Effect.gen(function* () { | |
| 295 | − const manifest = | |
| 296 | − input.revision.manifestJson !== null | |
| 297 | − ? yield* parseJsonValue<GraphqlToolManifest>({ | |
| 298 | − label: `GraphQL manifest for ${input.sourceRecord.id}`, | |
| 299 | − value: input.revision.manifestJson, | |
| 300 | − }) | |
| 301 | − : null; | |
| 302 | − const document = primaryDocument(input.documents); | |
| 303 | − const resolvedManifest = | |
| 304 | − manifest | |
| 305 | − ?? (document !== null | |
| 306 | − ? yield* extractGraphqlManifest( | |
| 307 | − input.sourceRecord.name, | |
| 308 | − document.contentText, | |
| 309 | − ) | |
| 310 | − : null); | |
| 311 | − | |
| 312 | − if (resolvedManifest === null) { | |
| 313 | − return yield* Effect.fail( | |
| 314 | − new Error( | |
| 315 | − `Missing GraphQL manifest and document for ${input.sourceRecord.id}`, | |
| 316 | − ), | |
| 317 | − ); | |
| 318 | − } | |
| 319 | − | |
| 320 | − const now = Math.max(input.revision.updatedAt, input.sourceRecord.updatedAt); | |
| 321 | − const definitions = compileGraphqlToolDefinitions(resolvedManifest); | |
| 322 | − const schemaBundle = toGraphqlSchemaBundleRecord({ | |
| 323 | − recipeRevisionId: input.revision.id, | |
| 324 | − manifest: resolvedManifest, | |
| 325 | − now, | |
| 326 | − }); | |
| 327 | − | |
| 328 | − yield* input.rows.sourceRecipeRevisions.update(input.revision.id, { | |
| 329 | − manifestJson: JSON.stringify(resolvedManifest), | |
| 330 | − manifestHash: resolvedManifest.sourceHash, | |
| 331 | − updatedAt: now, | |
| 332 | − }); | |
| 333 | − yield* input.rows.sourceRecipeOperations.replaceForRevision({ | |
| 334 | − recipeRevisionId: input.revision.id, | |
| 335 | − operations: definitions.map((definition) => | |
| 336 | − toGraphqlRecipeOperationRecord({ | |
| 337 | − recipeRevisionId: input.revision.id, | |
| 338 | − definition, | |
| 339 | − manifest: resolvedManifest, | |
| 340 | − now, | |
| 341 | − }), | |
| 342 | − ), | |
| 343 | − }); | |
| 344 | − yield* input.rows.sourceRecipeSchemaBundles.replaceForRevision({ | |
| 345 | − recipeRevisionId: input.revision.id, | |
| 346 | − bundles: schemaBundle ? [schemaBundle] : [], | |
| 347 | − }); | |
| 348 | − | |
| 349 | − if (input.sourceRecord.sourceHash !== resolvedManifest.sourceHash) { | |
| 350 | − yield* input.rows.sources.update(input.sourceRecord.workspaceId, input.sourceRecord.id, { | |
| 351 | − sourceHash: resolvedManifest.sourceHash, | |
| 352 | − updatedAt: now, | |
| 353 | − }); | |
| 354 | − } | |
| 355 | − }); | |
| 356 | − | |
| 357 | 286 | export const graphqlSourceAdapter: SourceAdapter = { |
| 358 | 287 | key: "graphql", |
| 359 | 288 | displayName: "GraphQL", |
| @@ -9,7 +9,6 @@ import { · 8 unmodified lines | ||
| 9 | 9 | type McpToolManifest, |
| 10 | 10 | type McpToolManifestEntry, |
| 11 | 11 | } from "@executor/codemode-mcp"; |
| 12 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 13 | 12 | import type { |
| 14 | 13 | Source, |
| 15 | 14 | SourceRecipeRevisionId, |
| @@ -25,7 +24,6 @@ import * as Schema from "effect/Schema"; · 9 unmodified lines | ||
| 25 | 24 | import { |
| 26 | 25 | contentHash, |
| 27 | 26 | normalizeSearchText, |
| 28 | − persistRecipeMaterialization, | |
| 29 | 27 | type SourceRecipeMaterialization, |
| 30 | 28 | } from "../source-recipe-support"; |
| 31 | 29 | import { namespaceFromSourceName } from "../source-names"; |
| @@ -170,23 +168,6 @@ const toMcpRecipeOperationRecord = (input: { · 138 unmodified lines | ||
| 170 | 168 | updatedAt: input.now, |
| 171 | 169 | }); |
| 172 | 170 | |
| 173 | −export const persistMcpRecipeRevisionFromManifestEntries = (input: { | |
| 174 | − rows: SqlControlPlaneRows; | |
| 175 | − source: Source; | |
| 176 | − manifestEntries: readonly McpToolManifestEntry[]; | |
| 177 | −}): Effect.Effect<void, Error, never> => | |
| 178 | − Effect.gen(function* () { | |
| 179 | − yield* persistRecipeMaterialization({ | |
| 180 | − rows: input.rows, | |
| 181 | − source: input.source, | |
| 182 | − materialization: materializationFromMcpManifestEntries({ | |
| 183 | − recipeRevisionId: "src_recipe_rev_materialization" as SourceRecipeRevisionId, | |
| 184 | − endpoint: input.source.endpoint, | |
| 185 | − manifestEntries: input.manifestEntries, | |
| 186 | − }), | |
| 187 | − }); | |
| 188 | − }); | |
| 189 | − | |
| 190 | 171 | export const materializationFromMcpManifestEntries = (input: { |
| 191 | 172 | recipeRevisionId: SourceRecipeRevisionId; |
| 192 | 173 | endpoint: string; |
| @@ -3,7 +3,6 @@ import { · 2 unmodified lines | ||
| 3 | 3 | HttpClient, |
| 4 | 4 | HttpClientRequest, |
| 5 | 5 | } from "@effect/platform"; |
| 6 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 7 | 6 | import { |
| 8 | 7 | applyCookiePlacementsToHeaders, |
| 9 | 8 | applyHttpQueryPlacementsToUrl, |
| @@ -41,7 +40,6 @@ import { · 31 unmodified lines | ||
| 41 | 40 | import type { |
| 42 | 41 | SourceAdapter, |
| 43 | 42 | SourceAdapterMaterialization, |
| 44 | − SourceAdapterRepairRevisionInput, | |
| 45 | 43 | } from "./types"; |
| 46 | 44 | import { |
| 47 | 45 | ConnectHttpAuthSchema, |
| @@ -330,74 +328,6 @@ const primaryDocument = ( · 282 unmodified lines | ||
| 330 | 328 | ): StoredSourceRecipeDocumentRecord | null => |
| 331 | 329 | documents.find((document) => document.documentKind === "openapi") ?? null; |
| 332 | 330 | |
| 333 | −export const rebuildOpenApiRecipeRevisionContent = (input: { | |
| 334 | − rows: SqlControlPlaneRows; | |
| 335 | − sourceRecord: SourceAdapterRepairRevisionInput["sourceRecord"]; | |
| 336 | − revision: SourceAdapterRepairRevisionInput["revision"]; | |
| 337 | − documents: readonly StoredSourceRecipeDocumentRecord[]; | |
| 338 | −}): Effect.Effect<void, Error, never> => | |
| 339 | − Effect.gen(function* () { | |
| 340 | − const manifest = | |
| 341 | − input.revision.manifestJson !== null | |
| 342 | − ? ((yield* parseJsonValue<OpenApiToolManifest>({ | |
| 343 | − label: `OpenAPI manifest for ${input.sourceRecord.id}`, | |
| 344 | − value: input.revision.manifestJson, | |
| 345 | − })) as OpenApiToolManifest | null) | |
| 346 | − : null; | |
| 347 | − const document = primaryDocument(input.documents); | |
| 348 | − const resolvedManifest = | |
| 349 | − manifest | |
| 350 | − ?? (document !== null | |
| 351 | − ? yield* extractOpenApiManifest( | |
| 352 | − input.sourceRecord.name, | |
| 353 | − document.contentText, | |
| 354 | − ) | |
| 355 | − : null); | |
| 356 | − | |
| 357 | − if (resolvedManifest === null) { | |
| 358 | − return yield* Effect.fail( | |
| 359 | − new Error( | |
| 360 | − `Missing OpenAPI manifest and document for ${input.sourceRecord.id}`, | |
| 361 | − ), | |
| 362 | − ); | |
| 363 | − } | |
| 364 | − | |
| 365 | − const now = Math.max(input.revision.updatedAt, input.sourceRecord.updatedAt); | |
| 366 | − const definitions = compileOpenApiToolDefinitions(resolvedManifest); | |
| 367 | − const schemaBundle = toOpenApiSchemaBundleRecord({ | |
| 368 | − recipeRevisionId: input.revision.id, | |
| 369 | − refTable: resolvedManifest.refHintTable, | |
| 370 | − now, | |
| 371 | − }); | |
| 372 | − | |
| 373 | − yield* input.rows.sourceRecipeRevisions.update(input.revision.id, { | |
| 374 | − manifestJson: JSON.stringify(resolvedManifest), | |
| 375 | − manifestHash: resolvedManifest.sourceHash, | |
| 376 | − updatedAt: now, | |
| 377 | − }); | |
| 378 | − yield* input.rows.sourceRecipeOperations.replaceForRevision({ | |
| 379 | − recipeRevisionId: input.revision.id, | |
| 380 | − operations: definitions.map((definition) => | |
| 381 | − toOpenApiRecipeOperationRecord({ | |
| 382 | − recipeRevisionId: input.revision.id, | |
| 383 | − definition, | |
| 384 | − now, | |
| 385 | − }), | |
| 386 | − ), | |
| 387 | − }); | |
| 388 | − yield* input.rows.sourceRecipeSchemaBundles.replaceForRevision({ | |
| 389 | − recipeRevisionId: input.revision.id, | |
| 390 | − bundles: schemaBundle ? [schemaBundle] : [], | |
| 391 | − }); | |
| 392 | − | |
| 393 | − if (input.sourceRecord.sourceHash !== resolvedManifest.sourceHash) { | |
| 394 | − yield* input.rows.sources.update(input.sourceRecord.workspaceId, input.sourceRecord.id, { | |
| 395 | − sourceHash: resolvedManifest.sourceHash, | |
| 396 | − updatedAt: now, | |
| 397 | − }); | |
| 398 | − } | |
| 399 | − }); | |
| 400 | − | |
| 401 | 331 | export const openApiSourceAdapter: SourceAdapter = { |
| 402 | 332 | key: "openapi", |
| 403 | 333 | displayName: "OpenAPI", |
| @@ -3,7 +3,6 @@ import type { · 2 unmodified lines | ||
| 3 | 3 | ToolDescriptor, |
| 4 | 4 | ToolSchemaBundle, |
| 5 | 5 | } from "@executor/codemode-core"; |
| 6 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 7 | 6 | import type { |
| 8 | 7 | AccountId, |
| 9 | 8 | CredentialSlot, |
| @@ -25,6 +24,7 @@ import * as Schema from "effect/Schema"; · 15 unmodified lines | ||
| 25 | 24 | |
| 26 | 25 | import type { ResolvedSourceAuthMaterial } from "../source-auth-material"; |
| 27 | 26 | import type { ResolveSecretMaterial } from "../secret-material-providers"; |
| 27 | +import type { ControlPlaneStoreShape } from "../store"; | |
| 28 | 28 | |
| 29 | 29 | export type SourceAdapterFamily = "http_api" | "mcp" | "internal"; |
| 30 | 30 | export type SourceAdapterInputSchema = Schema.Schema<any, any, never>; |
| @@ -81,17 +81,8 @@ export type SourceAdapterOauth2SetupConfig = { · 50 unmodified lines | ||
| 81 | 81 | authorizationParams?: Readonly<Record<string, string>>; |
| 82 | 82 | }; |
| 83 | 83 | |
| 84 | −export type SourceAdapterRepairRevisionInput = { | |
| 85 | − rows: SqlControlPlaneRows; | |
| 86 | − sourceRecord: StoredSourceRecord; | |
| 87 | − revision: StoredSourceRecipeRevisionRecord; | |
| 88 | − documents: readonly StoredSourceRecipeDocumentRecord[]; | |
| 89 | − operations: readonly StoredSourceRecipeOperationRecord[]; | |
| 90 | − schemaBundles: readonly ToolSchemaBundle[]; | |
| 91 | −}; | |
| 92 | − | |
| 93 | 84 | export type SourceAdapterInvokePersistedToolInput = { |
| 94 | − rows: SqlControlPlaneRows; | |
| 85 | + rows: ControlPlaneStoreShape; | |
| 95 | 86 | workspaceId: Source["workspaceId"]; |
| 96 | 87 | accountId: AccountId; |
| 97 | 88 | source: Source; |
| @@ -64,7 +64,7 @@ describe("source-auth-material", () => { · 63 unmodified lines | ||
| 64 | 64 | headerName: "X-Api-Key", |
| 65 | 65 | prefix: "Token ", |
| 66 | 66 | token: { |
| 67 | − providerId: "postgres", | |
| 67 | + providerId: "local", | |
| 68 | 68 | handle: "sec_bearer", |
| 69 | 69 | }, |
| 70 | 70 | }, |
| @@ -75,7 +75,7 @@ describe("source-auth-material", () => { · 4 unmodified lines | ||
| 75 | 75 | }, |
| 76 | 76 | })); |
| 77 | 77 | |
| 78 | − expect(calls).toEqual(["postgres:sec_bearer"]); | |
| 78 | + expect(calls).toEqual(["local:sec_bearer"]); | |
| 79 | 79 | expect(auth).toEqual({ |
| 80 | 80 | placements: [ |
| 81 | 81 | { |
| @@ -110,11 +110,11 @@ describe("source-auth-material", () => { · 28 unmodified lines | ||
| 110 | 110 | headerName: "Authorization", |
| 111 | 111 | prefix: "Bearer ", |
| 112 | 112 | accessToken: { |
| 113 | − providerId: "postgres", | |
| 113 | + providerId: "local", | |
| 114 | 114 | handle: "sec_access", |
| 115 | 115 | }, |
| 116 | 116 | refreshToken: { |
| 117 | − providerId: "postgres", | |
| 117 | + providerId: "local", | |
| 118 | 118 | handle: "sec_refresh", |
| 119 | 119 | }, |
| 120 | 120 | }, |
| @@ -125,7 +125,7 @@ describe("source-auth-material", () => { · 4 unmodified lines | ||
| 125 | 125 | }, |
| 126 | 126 | })); |
| 127 | 127 | |
| 128 | − expect(calls).toEqual(["postgres:sec_access"]); | |
| 128 | + expect(calls).toEqual(["local:sec_access"]); | |
| 129 | 129 | expect(auth).toEqual({ |
| 130 | 130 | placements: [ |
| 131 | 131 | { |
| @@ -1,4 +1,3 @@ | ||
| 1 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 2 | 1 | import type { |
| 3 | 2 | AccountId, |
| 4 | 3 | CredentialSlot, |
| @@ -8,6 +7,7 @@ import type { · 3 unmodified lines | ||
| 8 | 7 | import * as Effect from "effect/Effect"; |
| 9 | 8 | import * as Option from "effect/Option"; |
| 10 | 9 | |
| 10 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 11 | 11 | import { |
| 12 | 12 | authArtifactFromSourceAuth, |
| 13 | 13 | resolveAuthArtifactMaterial, |
| @@ -42,7 +42,7 @@ export const resolveSourceAuthMaterial = (input: { · 28 unmodified lines | ||
| 42 | 42 | source: Source; |
| 43 | 43 | slot?: CredentialSlot; |
| 44 | 44 | actorAccountId?: AccountId | null; |
| 45 | − rows?: SqlControlPlaneRows; | |
| 45 | + rows?: ControlPlaneStoreShape; | |
| 46 | 46 | resolveSecretMaterial: ResolveSecretMaterial; |
| 47 | 47 | context?: SecretMaterialResolveContext; |
| 48 | 48 | }): Effect.Effect<ResolvedSourceAuthMaterial, Error, never> => |
| @@ -1,608 +0,0 @@ | ||
| 1 | −import { mkdtempSync } from "node:fs"; | |
| 2 | −import { createServer } from "node:http"; | |
| 3 | −import { tmpdir } from "node:os"; | |
| 4 | −import { join } from "node:path"; | |
| 5 | − | |
| 6 | −import { describe, expect, it } from "vitest"; | |
| 7 | −import * as Schema from "effect/Schema"; | |
| 8 | −import * as Effect from "effect/Effect"; | |
| 9 | −import * as Option from "effect/Option"; | |
| 10 | − | |
| 11 | −import { | |
| 12 | − AccountIdSchema, | |
| 13 | − McpSourceAuthSessionDataJsonSchema, | |
| 14 | − OAuth2PkceSourceAuthSessionDataJsonSchema, | |
| 15 | − SecretMaterialIdSchema, | |
| 16 | − type Source, | |
| 17 | − type SourceAuthSession, | |
| 18 | − WorkspaceIdSchema, | |
| 19 | − decodeAuthLeasePlacementTemplates, | |
| 20 | − decodeBuiltInAuthArtifactConfig, | |
| 21 | −} from "#schema"; | |
| 22 | −import { | |
| 23 | − createSqlControlPlanePersistence, | |
| 24 | − type SqlControlPlanePersistence, | |
| 25 | −} from "#persistence"; | |
| 26 | − | |
| 27 | −import { | |
| 28 | − createRuntimeSourceAuthService, | |
| 29 | − createTerminalSourceAuthSessionPatch, | |
| 30 | −} from "./source-auth-service"; | |
| 31 | −import { createLiveExecutionManager } from "./live-execution"; | |
| 32 | −import { | |
| 33 | − loadLocalExecutorConfig, | |
| 34 | − resolveLocalWorkspaceContext, | |
| 35 | −} from "./local-config"; | |
| 36 | −import { | |
| 37 | − readLocalSourceArtifact, | |
| 38 | −} from "./local-source-artifacts"; | |
| 39 | −import { | |
| 40 | − RuntimeLocalWorkspaceService, | |
| 41 | − type RuntimeLocalWorkspaceState, | |
| 42 | −} from "./local-runtime-context"; | |
| 43 | −import { createDefaultSecretMaterialResolver } from "./secret-material-providers"; | |
| 44 | −import { resolveSourceAuthMaterial } from "./source-auth-material"; | |
| 45 | − | |
| 46 | −const makeExistingOpenApiSource = (auth: Source["auth"]): Source => ({ | |
| 47 | − id: "src_test" as Source["id"], | |
| 48 | − workspaceId: "ws_test" as Source["workspaceId"], | |
| 49 | − name: "GitHub", | |
| 50 | − kind: "openapi", | |
| 51 | − endpoint: "https://api.github.com", | |
| 52 | − status: "connected", | |
| 53 | − enabled: true, | |
| 54 | − namespace: "github", | |
| 55 | − bindingVersion: 1, | |
| 56 | − binding: { | |
| 57 | − specUrl: "https://example.com/openapi.json", | |
| 58 | − defaultHeaders: null, | |
| 59 | − }, | |
| 60 | − importAuthPolicy: "reuse_runtime", | |
| 61 | − importAuth: { kind: "none" }, | |
| 62 | − auth, | |
| 63 | − sourceHash: null, | |
| 64 | − lastError: null, | |
| 65 | − createdAt: 1, | |
| 66 | − updatedAt: 1, | |
| 67 | −}); | |
| 68 | − | |
| 69 | −const fetchLiveDiscoveryDocument = async (): Promise<string> => { | |
| 70 | − const response = await fetch("https://www.googleapis.com/discovery/v1/apis/sheets/v4/rest", { | |
| 71 | − signal: AbortSignal.timeout(20_000), | |
| 72 | − }); | |
| 73 | − if (!response.ok) { | |
| 74 | − throw new Error(`Failed to fetch Sheets discovery doc: ${response.status}`); | |
| 75 | − } | |
| 76 | − | |
| 77 | − return response.text(); | |
| 78 | −}; | |
| 79 | − | |
| 80 | −const withGoogleAuthTestServer = async <T>(input: { | |
| 81 | − discoveryDocument: string; | |
| 82 | − handler: (server: { | |
| 83 | − discoveryUrl: string; | |
| 84 | − tokenUrl: string; | |
| 85 | − tokenRequests: Array<URLSearchParams>; | |
| 86 | − }) => Promise<T>; | |
| 87 | −}): Promise<T> => { | |
| 88 | − const tokenRequests: Array<URLSearchParams> = []; | |
| 89 | − const server = createServer(async (request, response) => { | |
| 90 | − const requestUrl = new URL( | |
| 91 | − request.url ?? "/", | |
| 92 | − "http://127.0.0.1", | |
| 93 | − ); | |
| 94 | − | |
| 95 | − if (request.method === "GET" && requestUrl.pathname === "/discovery") { | |
| 96 | − response.statusCode = 200; | |
| 97 | − response.setHeader("content-type", "application/json"); | |
| 98 | − response.end(input.discoveryDocument); | |
| 99 | − return; | |
| 100 | − } | |
| 101 | − | |
| 102 | − if (request.method === "POST" && requestUrl.pathname === "/token") { | |
| 103 | − const chunks: Buffer[] = []; | |
| 104 | − for await (const chunk of request) { | |
| 105 | − chunks.push(Buffer.from(chunk)); | |
| 106 | − } | |
| 107 | − const form = new URLSearchParams(Buffer.concat(chunks).toString("utf8")); | |
| 108 | − tokenRequests.push(form); | |
| 109 | − | |
| 110 | − response.statusCode = 200; | |
| 111 | − response.setHeader("content-type", "application/json"); | |
| 112 | − if (form.get("grant_type") === "authorization_code") { | |
| 113 | − response.end( | |
| 114 | − JSON.stringify({ | |
| 115 | − access_token: "access-token-initial", | |
| 116 | − refresh_token: "refresh-token-live", | |
| 117 | − expires_in: 3600, | |
| 118 | − scope: "https://www.googleapis.com/auth/spreadsheets.readonly", | |
| 119 | − token_type: "Bearer", | |
| 120 | − }), | |
| 121 | − ); | |
| 122 | − return; | |
| 123 | − } | |
| 124 | − | |
| 125 | − response.end( | |
| 126 | − JSON.stringify({ | |
| 127 | − access_token: "access-token-refreshed", | |
| 128 | − expires_in: 3600, | |
| 129 | − token_type: "Bearer", | |
| 130 | − }), | |
| 131 | − ); | |
| 132 | − return; | |
| 133 | − } | |
| 134 | − | |
| 135 | − response.statusCode = 404; | |
| 136 | − response.end(); | |
| 137 | − }); | |
| 138 | − | |
| 139 | − await new Promise<void>((resolve, reject) => { | |
| 140 | − server.listen(0, "127.0.0.1", (error?: Error) => { | |
| 141 | − if (error) { | |
| 142 | − reject(error); | |
| 143 | − return; | |
| 144 | − } | |
| 145 | − resolve(); | |
| 146 | − }); | |
| 147 | − }); | |
| 148 | − | |
| 149 | − try { | |
| 150 | − const address = server.address(); | |
| 151 | − if (!address || typeof address === "string") { | |
| 152 | − throw new Error("Failed to resolve Google auth test server address"); | |
| 153 | − } | |
| 154 | − const baseUrl = `http://127.0.0.1:${address.port}`; | |
| 155 | − return await input.handler({ | |
| 156 | − discoveryUrl: `${baseUrl}/discovery`, | |
| 157 | − tokenUrl: `${baseUrl}/token`, | |
| 158 | − tokenRequests, | |
| 159 | − }); | |
| 160 | − } finally { | |
| 161 | − await new Promise<void>((resolve, reject) => { | |
| 162 | − server.close((error) => { | |
| 163 | − if (error) { | |
| 164 | − reject(error); | |
| 165 | − return; | |
| 166 | − } | |
| 167 | − resolve(); | |
| 168 | − }); | |
| 169 | − }); | |
| 170 | − } | |
| 171 | −}; | |
| 172 | − | |
| 173 | −const makePersistence = async (): Promise<SqlControlPlanePersistence> => | |
| 174 | − Effect.runPromise( | |
| 175 | − createSqlControlPlanePersistence({ | |
| 176 | − localDataDir: ":memory:", | |
| 177 | − }), | |
| 178 | − ); | |
| 179 | − | |
| 180 | −const makeRuntimeLocalWorkspaceState = async ( | |
| 181 | − workspaceId: ReturnType<typeof WorkspaceIdSchema.make>, | |
| 182 | − accountId: ReturnType<typeof AccountIdSchema.make>, | |
| 183 | −): Promise<RuntimeLocalWorkspaceState> => | |
| 184 | − Effect.runPromise( | |
| 185 | − Effect.gen(function* () { | |
| 186 | − const workspaceRoot = mkdtempSync( | |
| 187 | − join(tmpdir(), "executor-source-auth-service-"), | |
| 188 | − ); | |
| 189 | − const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 190 | − const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 191 | − | |
| 192 | − return { | |
| 193 | − context, | |
| 194 | − installation: { | |
| 195 | − workspaceId, | |
| 196 | − accountId, | |
| 197 | − }, | |
| 198 | − loadedConfig, | |
| 199 | − } satisfies RuntimeLocalWorkspaceState; | |
| 200 | − }), | |
| 201 | − ); | |
| 202 | − | |
| 203 | −describe("source-auth-service", () => { | |
| 204 | − const encodeSessionDataJson = Schema.encodeSync(McpSourceAuthSessionDataJsonSchema); | |
| 205 | − | |
| 206 | − const baseSessionDataJson = encodeSessionDataJson({ | |
| 207 | − kind: "mcp_oauth", | |
| 208 | − endpoint: "https://example.com/resource", | |
| 209 | − redirectUri: "http://127.0.0.1/callback", | |
| 210 | − scope: null, | |
| 211 | − resourceMetadataUrl: "https://example.com/resource", | |
| 212 | − authorizationServerUrl: "https://example.com/as", | |
| 213 | − resourceMetadata: { | |
| 214 | − issuer: "https://example.com", | |
| 215 | − }, | |
| 216 | − authorizationServerMetadata: { | |
| 217 | − token_endpoint: "https://example.com/token", | |
| 218 | − }, | |
| 219 | − clientInformation: { | |
| 220 | − client_id: "abc", | |
| 221 | − }, | |
| 222 | − codeVerifier: "verifier", | |
| 223 | − authorizationUrl: "https://example.com/auth", | |
| 224 | − }); | |
| 225 | − | |
| 226 | − it("clears ephemeral OAuth session fields when failing a session", () => { | |
| 227 | − const patch = createTerminalSourceAuthSessionPatch({ | |
| 228 | − sessionDataJson: baseSessionDataJson, | |
| 229 | − status: "failed", | |
| 230 | − now: 123, | |
| 231 | − errorText: "OAuth authorization failed", | |
| 232 | − }); | |
| 233 | − | |
| 234 | − expect(patch).toMatchObject({ | |
| 235 | − sessionDataJson: baseSessionDataJson, | |
| 236 | − status: "failed", | |
| 237 | − errorText: "OAuth authorization failed", | |
| 238 | − completedAt: 123, | |
| 239 | − updatedAt: 123, | |
| 240 | − }); | |
| 241 | − }); | |
| 242 | − | |
| 243 | − it("clears ephemeral OAuth session fields when completing a session", () => { | |
| 244 | − const patch = createTerminalSourceAuthSessionPatch({ | |
| 245 | − sessionDataJson: baseSessionDataJson, | |
| 246 | − status: "completed", | |
| 247 | − now: 456, | |
| 248 | − errorText: null, | |
| 249 | − }); | |
| 250 | − | |
| 251 | − expect(patch).toMatchObject({ | |
| 252 | − sessionDataJson: baseSessionDataJson, | |
| 253 | − status: "completed", | |
| 254 | − errorText: null, | |
| 255 | − completedAt: 456, | |
| 256 | − updatedAt: 456, | |
| 257 | − }); | |
| 258 | − }); | |
| 259 | − | |
| 260 | − it("completes Google Discovery desktop OAuth into a refreshable auth artifact and lease", async () => { | |
| 261 | − const discoveryDocument = await fetchLiveDiscoveryDocument(); | |
| 262 | − | |
| 263 | − const persistence = await makePersistence(); | |
| 264 | − try { | |
| 265 | − const workspaceId = WorkspaceIdSchema.make("ws_google_oauth"); | |
| 266 | − const accountId = AccountIdSchema.make("acc_google_oauth"); | |
| 267 | − const runtimeLocalWorkspace = await makeRuntimeLocalWorkspaceState( | |
| 268 | − workspaceId, | |
| 269 | − accountId, | |
| 270 | − ); | |
| 271 | − | |
| 272 | − const service = createRuntimeSourceAuthService({ | |
| 273 | − rows: persistence.rows, | |
| 274 | − liveExecutionManager: createLiveExecutionManager(), | |
| 275 | − getLocalServerBaseUrl: () => "http://localhost:8788", | |
| 276 | − localConfig: runtimeLocalWorkspace.loadedConfig.config, | |
| 277 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 278 | − localWorkspaceState: runtimeLocalWorkspace, | |
| 279 | − }); | |
| 280 | − | |
| 281 | − await withGoogleAuthTestServer({ | |
| 282 | − discoveryDocument, | |
| 283 | − handler: async ({ discoveryUrl, tokenUrl, tokenRequests }) => { | |
| 284 | − const originalFetch = globalThis.fetch; | |
| 285 | − globalThis.fetch = (async (input, init) => { | |
| 286 | − const url = typeof input === "string" | |
| 287 | − ? input | |
| 288 | − : input instanceof URL | |
| 289 | − ? input.toString() | |
| 290 | − : input.url; | |
| 291 | − if (url === "https://oauth2.googleapis.com/token") { | |
| 292 | − return originalFetch(tokenUrl, init); | |
| 293 | − } | |
| 294 | − | |
| 295 | − return originalFetch(input as any, init); | |
| 296 | − }) as typeof fetch; | |
| 297 | − | |
| 298 | − try { | |
| 299 | − const addResult = await Effect.runPromise( | |
| 300 | − service.addExecutorSource({ | |
| 301 | − kind: "google_discovery", | |
| 302 | − workspaceId, | |
| 303 | − actorAccountId: accountId, | |
| 304 | − executionId: null, | |
| 305 | − interactionId: null, | |
| 306 | − service: "sheets", | |
| 307 | − version: "v4", | |
| 308 | − discoveryUrl, | |
| 309 | − scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"], | |
| 310 | − oauthClient: { | |
| 311 | − clientId: "google-test-client", | |
| 312 | − clientSecret: "google-test-secret", | |
| 313 | − }, | |
| 314 | − }).pipe( | |
| 315 | − Effect.provideService( | |
| 316 | − RuntimeLocalWorkspaceService, | |
| 317 | − runtimeLocalWorkspace, | |
| 318 | − ), | |
| 319 | − ), | |
| 320 | − ); | |
| 321 | − | |
| 322 | − expect(addResult.kind).toBe("oauth_required"); | |
| 323 | − if (addResult.kind !== "oauth_required") { | |
| 324 | − return; | |
| 325 | − } | |
| 326 | − | |
| 327 | − const sessionOption = await Effect.runPromise( | |
| 328 | − persistence.rows.sourceAuthSessions.getById(addResult.sessionId), | |
| 329 | − ); | |
| 330 | − expect(Option.isSome(sessionOption)).toBe(true); | |
| 331 | − const session = Option.getOrNull(sessionOption) as SourceAuthSession; | |
| 332 | − const oauthClientOption = await Effect.runPromise( | |
| 333 | − persistence.rows.sourceOauthClients.getByWorkspaceSourceAndProvider({ | |
| 334 | − workspaceId, | |
| 335 | − sourceId: addResult.source.id, | |
| 336 | − providerKey: "google_workspace", | |
| 337 | − }), | |
| 338 | − ); | |
| 339 | − expect(Option.isSome(oauthClientOption)).toBe(true); | |
| 340 | − const oauthClient = Option.getOrNull(oauthClientOption)!; | |
| 341 | − expect(oauthClient.clientId).toBe("google-test-client"); | |
| 342 | − expect(oauthClient.clientSecretProviderId).toBe("postgres"); | |
| 343 | − expect(oauthClient.clientSecretHandle).toBeTruthy(); | |
| 344 | − expect(session.providerKind).toBe("oauth2_pkce"); | |
| 345 | − const sessionData = Schema.decodeUnknownSync( | |
| 346 | − OAuth2PkceSourceAuthSessionDataJsonSchema, | |
| 347 | − )(session.sessionDataJson); | |
| 348 | − expect(sessionData.redirectUri).toMatch(/^http:\/\/127\.0\.0\.1:\d+$/); | |
| 349 | − expect(sessionData.tokenEndpoint).toBe("https://oauth2.googleapis.com/token"); | |
| 350 | − expect(sessionData.authorizationUrl).toContain( | |
| 351 | − encodeURIComponent(sessionData.redirectUri), | |
| 352 | − ); | |
| 353 | − | |
| 354 | − const loopbackResponse = await fetch( | |
| 355 | − `${sessionData.redirectUri}/?state=${encodeURIComponent(session.state)}&code=loopback-code`, | |
| 356 | − { | |
| 357 | − redirect: "manual", | |
| 358 | − }, | |
| 359 | − ); | |
| 360 | − expect(loopbackResponse.status).toBe(302); | |
| 361 | − const completionLocation = loopbackResponse.headers.get("location"); | |
| 362 | − expect(completionLocation).toBeTruthy(); | |
| 363 | − if (!completionLocation) { | |
| 364 | − return; | |
| 365 | − } | |
| 366 | − const completionUrl = new URL(completionLocation); | |
| 367 | − expect(completionUrl.origin).toBe("http://localhost:8788"); | |
| 368 | − expect(completionUrl.searchParams.get("state")).toBe(session.state); | |
| 369 | − expect(completionUrl.searchParams.get("code")).toBe("loopback-code"); | |
| 370 | − | |
| 371 | − const connectedSource = await Effect.runPromise( | |
| 372 | − service.completeSourceCredentialSetup({ | |
| 373 | − workspaceId, | |
| 374 | − sourceId: addResult.source.id, | |
| 375 | − actorAccountId: accountId, | |
| 376 | − state: session.state, | |
| 377 | − code: "authorization-code", | |
| 378 | − }), | |
| 379 | − ); | |
| 380 | − | |
| 381 | − expect(connectedSource.status).toBe("connected"); | |
| 382 | − expect(connectedSource.auth.kind).toBe("oauth2_authorized_user"); | |
| 383 | − if (connectedSource.auth.kind !== "oauth2_authorized_user") { | |
| 384 | − return; | |
| 385 | − } | |
| 386 | − expect(connectedSource.auth.clientId).toBe("google-test-client"); | |
| 387 | − expect(connectedSource.auth.tokenEndpoint).toBe("https://oauth2.googleapis.com/token"); | |
| 388 | − expect(connectedSource.auth.grantSet).toEqual([ | |
| 389 | − "https://www.googleapis.com/auth/spreadsheets.readonly", | |
| 390 | − ]); | |
| 391 | − | |
| 392 | − expect(tokenRequests).toHaveLength(1); | |
| 393 | − expect(tokenRequests[0]?.get("grant_type")).toBe("authorization_code"); | |
| 394 | − expect(tokenRequests[0]?.get("client_id")).toBe("google-test-client"); | |
| 395 | − expect(tokenRequests[0]?.get("client_secret")).toBe("google-test-secret"); | |
| 396 | − expect(tokenRequests[0]?.get("code")).toBe("authorization-code"); | |
| 397 | − expect(tokenRequests[0]?.get("redirect_uri")).toBe(sessionData.redirectUri); | |
| 398 | − expect(tokenRequests[0]?.get("code_verifier")).toBe(sessionData.codeVerifier); | |
| 399 | − | |
| 400 | − const artifactOption = await Effect.runPromise( | |
| 401 | − persistence.rows.authArtifacts.getByWorkspaceSourceAndActor({ | |
| 402 | − workspaceId, | |
| 403 | − sourceId: connectedSource.id, | |
| 404 | − actorAccountId: accountId, | |
| 405 | − slot: "runtime", | |
| 406 | − }), | |
| 407 | − ); | |
| 408 | − expect(Option.isSome(artifactOption)).toBe(true); | |
| 409 | − const artifact = Option.getOrNull(artifactOption)!; | |
| 410 | − const decodedArtifact = decodeBuiltInAuthArtifactConfig(artifact); | |
| 411 | − expect(decodedArtifact?.artifactKind).toBe("oauth2_authorized_user"); | |
| 412 | − | |
| 413 | − const leaseOption = await Effect.runPromise( | |
| 414 | − persistence.rows.authLeases.getByAuthArtifactId(artifact.id), | |
| 415 | − ); | |
| 416 | − expect(Option.isSome(leaseOption)).toBe(true); | |
| 417 | − const initialLease = Option.getOrNull(leaseOption)!; | |
| 418 | − const initialTemplates = decodeAuthLeasePlacementTemplates(initialLease); | |
| 419 | − expect(initialTemplates).toEqual([ | |
| 420 | − { | |
| 421 | − location: "header", | |
| 422 | − name: "Authorization", | |
| 423 | − parts: [ | |
| 424 | − { | |
| 425 | − kind: "literal", | |
| 426 | − value: "Bearer ", | |
| 427 | − }, | |
| 428 | − { | |
| 429 | − kind: "secret_ref", | |
| 430 | − ref: { | |
| 431 | − providerId: "postgres", | |
| 432 | − handle: expect.any(String), | |
| 433 | − }, | |
| 434 | − }, | |
| 435 | − ], | |
| 436 | − }, | |
| 437 | − ]); | |
| 438 | − | |
| 439 | − const resolver = createDefaultSecretMaterialResolver({ | |
| 440 | − rows: persistence.rows, | |
| 441 | − }); | |
| 442 | − const resolvedInitialAuth = await Effect.runPromise( | |
| 443 | − resolveSourceAuthMaterial({ | |
| 444 | − source: connectedSource, | |
| 445 | − slot: "runtime", | |
| 446 | − actorAccountId: accountId, | |
| 447 | − rows: persistence.rows, | |
| 448 | − resolveSecretMaterial: resolver, | |
| 449 | − }), | |
| 450 | − ); | |
| 451 | − expect(resolvedInitialAuth.headers.Authorization).toBe( | |
| 452 | − "Bearer access-token-initial", | |
| 453 | − ); | |
| 454 | − | |
| 455 | − await Effect.runPromise( | |
| 456 | − persistence.rows.authLeases.upsert({ | |
| 457 | − ...initialLease, | |
| 458 | − refreshAfter: Date.now() - 1, | |
| 459 | − expiresAt: Date.now() + 10_000, | |
| 460 | − updatedAt: Date.now(), | |
| 461 | − }), | |
| 462 | − ); | |
| 463 | − | |
| 464 | − const previousAccessTokenHandle = initialTemplates?.[0]?.parts[1]; | |
| 465 | − const refreshedAuth = await Effect.runPromise( | |
| 466 | − resolveSourceAuthMaterial({ | |
| 467 | − source: connectedSource, | |
| 468 | − slot: "runtime", | |
| 469 | − actorAccountId: accountId, | |
| 470 | − rows: persistence.rows, | |
| 471 | − resolveSecretMaterial: resolver, | |
| 472 | − }), | |
| 473 | − ); | |
| 474 | − expect(refreshedAuth.headers.Authorization).toBe( | |
| 475 | − "Bearer access-token-refreshed", | |
| 476 | − ); | |
| 477 | − expect(tokenRequests).toHaveLength(2); | |
| 478 | − expect(tokenRequests[1]?.get("grant_type")).toBe("refresh_token"); | |
| 479 | − expect(tokenRequests[1]?.get("refresh_token")).toBe("refresh-token-live"); | |
| 480 | − | |
| 481 | − if (previousAccessTokenHandle?.kind === "secret_ref") { | |
| 482 | − const oldSecret = await Effect.runPromise( | |
| 483 | − persistence.rows.secretMaterials.getById( | |
| 484 | − SecretMaterialIdSchema.make(previousAccessTokenHandle.ref.handle), | |
| 485 | − ), | |
| 486 | − ); | |
| 487 | − expect(Option.isNone(oldSecret)).toBe(true); | |
| 488 | − } | |
| 489 | − | |
| 490 | − const localArtifact = await Effect.runPromise(readLocalSourceArtifact({ | |
| 491 | − context: runtimeLocalWorkspace.context, | |
| 492 | − sourceId: connectedSource.id, | |
| 493 | − })); | |
| 494 | − expect(localArtifact).not.toBeNull(); | |
| 495 | − expect(localArtifact?.operations.length).toBeGreaterThan(0); | |
| 496 | − expect( | |
| 497 | − localArtifact?.operations.some( | |
| 498 | − (operation) => operation.toolId === "spreadsheets.values.get", | |
| 499 | − ), | |
| 500 | − ).toBe(true); | |
| 501 | − } finally { | |
| 502 | − globalThis.fetch = originalFetch; | |
| 503 | − } | |
| 504 | − }, | |
| 505 | − }); | |
| 506 | − } finally { | |
| 507 | − await persistence.close(); | |
| 508 | − } | |
| 509 | − }, 60_000); | |
| 510 | − | |
| 511 | − it("uses the request origin callback for Google Discovery OAuth started from the web app", async () => { | |
| 512 | − const discoveryDocument = await fetchLiveDiscoveryDocument(); | |
| 513 | − | |
| 514 | − const persistence = await makePersistence(); | |
| 515 | − try { | |
| 516 | − const workspaceId = WorkspaceIdSchema.make("ws_google_oauth_web"); | |
| 517 | − const accountId = AccountIdSchema.make("acc_google_oauth_web"); | |
| 518 | − const runtimeLocalWorkspace = await makeRuntimeLocalWorkspaceState( | |
| 519 | − workspaceId, | |
| 520 | − accountId, | |
| 521 | − ); | |
| 522 | − | |
| 523 | − const service = createRuntimeSourceAuthService({ | |
| 524 | − rows: persistence.rows, | |
| 525 | − liveExecutionManager: createLiveExecutionManager(), | |
| 526 | − getLocalServerBaseUrl: () => "http://127.0.0.1:8788", | |
| 527 | − localConfig: runtimeLocalWorkspace.loadedConfig.config, | |
| 528 | − workspaceRoot: runtimeLocalWorkspace.context.workspaceRoot, | |
| 529 | − localWorkspaceState: runtimeLocalWorkspace, | |
| 530 | − }); | |
| 531 | − | |
| 532 | − await withGoogleAuthTestServer({ | |
| 533 | − discoveryDocument, | |
| 534 | − handler: async ({ discoveryUrl, tokenUrl }) => { | |
| 535 | − const originalFetch = globalThis.fetch; | |
| 536 | − globalThis.fetch = (async (input, init) => { | |
| 537 | − const url = typeof input === "string" | |
| 538 | − ? input | |
| 539 | − : input instanceof URL | |
| 540 | − ? input.toString() | |
| 541 | − : input.url; | |
| 542 | − if (url === "https://oauth2.googleapis.com/token") { | |
| 543 | − return originalFetch(tokenUrl, init); | |
| 544 | − } | |
| 545 | − | |
| 546 | − return originalFetch(input as any, init); | |
| 547 | − }) as typeof fetch; | |
| 548 | − | |
| 549 | − try { | |
| 550 | − const addResult = await Effect.runPromise( | |
| 551 | − service.addExecutorSource( | |
| 552 | − { | |
| 553 | − kind: "google_discovery", | |
| 554 | − workspaceId, | |
| 555 | − actorAccountId: accountId, | |
| 556 | − executionId: null, | |
| 557 | − interactionId: null, | |
| 558 | − service: "sheets", | |
| 559 | − version: "v4", | |
| 560 | − discoveryUrl, | |
| 561 | − scopes: ["https://www.googleapis.com/auth/spreadsheets.readonly"], | |
| 562 | − oauthClient: { | |
| 563 | − clientId: "google-test-client", | |
| 564 | − clientSecret: "google-test-secret", | |
| 565 | − }, | |
| 566 | − }, | |
| 567 | − { | |
| 568 | − baseUrl: "https://app.executor.dev", | |
| 569 | − }, | |
| 570 | − ).pipe( | |
| 571 | − Effect.provideService( | |
| 572 | − RuntimeLocalWorkspaceService, | |
| 573 | − runtimeLocalWorkspace, | |
| 574 | − ), | |
| 575 | − ), | |
| 576 | − ); | |
| 577 | − | |
| 578 | − expect(addResult.kind).toBe("oauth_required"); | |
| 579 | − if (addResult.kind !== "oauth_required") { | |
| 580 | − return; | |
| 581 | − } | |
| 582 | − | |
| 583 | − const sessionOption = await Effect.runPromise( | |
| 584 | − persistence.rows.sourceAuthSessions.getById(addResult.sessionId), | |
| 585 | − ); | |
| 586 | − expect(Option.isSome(sessionOption)).toBe(true); | |
| 587 | − const session = Option.getOrNull(sessionOption) as SourceAuthSession; | |
| 588 | − const sessionData = Schema.decodeUnknownSync( | |
| 589 | − OAuth2PkceSourceAuthSessionDataJsonSchema, | |
| 590 | − )(session.sessionDataJson); | |
| 591 | − | |
| 592 | − expect(sessionData.redirectUri).toBe( | |
| 593 | − `https://app.executor.dev/v1/workspaces/${encodeURIComponent(workspaceId)}/sources/${encodeURIComponent(addResult.source.id)}/credentials/oauth/complete`, | |
| 594 | − ); | |
| 595 | − expect(sessionData.authorizationUrl).toContain( | |
| 596 | − encodeURIComponent(sessionData.redirectUri), | |
| 597 | − ); | |
| 598 | − expect(sessionData.redirectUri).not.toMatch(/^http:\/\/127\.0\.0\.1:\d+$/); | |
| 599 | − } finally { | |
| 600 | − globalThis.fetch = originalFetch; | |
| 601 | − } | |
| 602 | − }, | |
| 603 | − }); | |
| 604 | − } finally { | |
| 605 | − await persistence.close(); | |
| 606 | − } | |
| 607 | − }, 60_000); | |
| 608 | −}); | |
| @@ -3,10 +3,6 @@ import { · 2 unmodified lines | ||
| 3 | 3 | discoverMcpToolsFromConnector, |
| 4 | 4 | type McpDiscoveryElicitationContext, |
| 5 | 5 | } from "@executor/codemode-mcp"; |
| 6 | −import { | |
| 7 | − SqlControlPlaneRowsService, | |
| 8 | − type SqlControlPlaneRows, | |
| 9 | −} from "#persistence"; | |
| 10 | 6 | import { |
| 11 | 7 | AccountId, |
| 12 | 8 | type CredentialSlot, |
| @@ -86,6 +82,8 @@ import { · 73 unmodified lines | ||
| 86 | 82 | loadSourcesInWorkspace, |
| 87 | 83 | persistSource, |
| 88 | 84 | } from "./source-store"; |
| 85 | +import type { WorkspaceStorageServices } from "./local-storage"; | |
| 86 | +import { ControlPlaneStore, type ControlPlaneStoreShape } from "./store"; | |
| 89 | 87 | |
| 90 | 88 | const trimOrNull = (value: string | null | undefined): string | null => { |
| 91 | 89 | if (value === null || value === undefined) { |
| @@ -303,7 +301,7 @@ const mergeOauth2PkceSourceAuthSessionData = (input: { · 211 unmodified lines | ||
| 303 | 301 | |
| 304 | 302 | |
| 305 | 303 | const completeLiveInteraction = (input: { |
| 306 | − rows: SqlControlPlaneRows; | |
| 304 | + rows: ControlPlaneStoreShape; | |
| 307 | 305 | liveExecutionManager: LiveExecutionManager; |
| 308 | 306 | session: SourceAuthSession; |
| 309 | 307 | response: { |
| @@ -360,7 +358,7 @@ const serializeJson = (value: unknown): string | null => { · 50 unmodified lines | ||
| 360 | 358 | return JSON.stringify(value); |
| 361 | 359 | }; |
| 362 | 360 | |
| 363 | −const updateSourceStatus = (rows: SqlControlPlaneRows, source: Source, input: { | |
| 361 | +const updateSourceStatus = (rows: ControlPlaneStoreShape, source: Source, input: { | |
| 364 | 362 | actorAccountId?: AccountId | null; |
| 365 | 363 | status: Source["status"]; |
| 366 | 364 | lastError?: string | null; |
| @@ -743,7 +741,7 @@ const sourceOauthClientSecretRef = (client: { · 376 unmodified lines | ||
| 743 | 741 | : null; |
| 744 | 742 | |
| 745 | 743 | const upsertSourceOauthClient = (input: { |
| 746 | − rows: SqlControlPlaneRows; | |
| 744 | + rows: ControlPlaneStoreShape; | |
| 747 | 745 | source: Source; |
| 748 | 746 | oauthClient: SourceOauthClientInput; |
| 749 | 747 | storeSecretMaterial: StoreSecretMaterial; |
| @@ -825,7 +823,7 @@ const upsertSourceOauthClient = (input: { · 75 unmodified lines | ||
| 825 | 823 | }); |
| 826 | 824 | |
| 827 | 825 | const resolveExistingSourceOauthClient = (input: { |
| 828 | − rows: SqlControlPlaneRows; | |
| 826 | + rows: ControlPlaneStoreShape; | |
| 829 | 827 | source: Source; |
| 830 | 828 | }): Effect.Effect<ResolvedSourceOauthClient | null, Error, never> => |
| 831 | 829 | Effect.gen(function* () { |
| @@ -858,7 +856,7 @@ const resolveExistingSourceOauthClient = (input: { · 26 unmodified lines | ||
| 858 | 856 | }); |
| 859 | 857 | |
| 860 | 858 | const startOauth2PkceSourceCredentialSetup = (input: { |
| 861 | − rows: SqlControlPlaneRows; | |
| 859 | + rows: ControlPlaneStoreShape; | |
| 862 | 860 | source: Source; |
| 863 | 861 | actorAccountId?: AccountId | null; |
| 864 | 862 | executionId?: SourceAuthSession["executionId"]; |
| @@ -866,7 +864,11 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 1 unmodified line | ||
| 866 | 864 | baseUrl: string; |
| 867 | 865 | redirectModeOverride?: WorkspaceSourceOauthClientRedirectMode; |
| 868 | 866 | storeSecretMaterial: StoreSecretMaterial; |
| 869 | −}): Effect.Effect<Extract<ExecutorSourceAddResult, { kind: "oauth_required" }> | null, Error, never> => | |
| 867 | +}): Effect.Effect< | |
| 868 | + Extract<ExecutorSourceAddResult, { kind: "oauth_required" }> | null, | |
| 869 | + Error, | |
| 870 | + WorkspaceStorageServices | |
| 871 | +> => | |
| 870 | 872 | Effect.gen(function* () { |
| 871 | 873 | const adapter = getSourceAdapterForSource(input.source); |
| 872 | 874 | const setupConfig = adapter.getOauth2SetupConfig |
| @@ -971,7 +973,7 @@ const startOauth2PkceSourceCredentialSetup = (input: { · 98 unmodified lines | ||
| 971 | 973 | }); |
| 972 | 974 | |
| 973 | 975 | const connectMcpSourceInternal = (input: { |
| 974 | − rows: SqlControlPlaneRows; | |
| 976 | + rows: ControlPlaneStoreShape; | |
| 975 | 977 | getLocalServerBaseUrl?: () => string | undefined; |
| 976 | 978 | baseUrl?: string | null; |
| 977 | 979 | workspaceId: WorkspaceId; |
| @@ -988,7 +990,7 @@ const connectMcpSourceInternal = (input: { · 10 unmodified lines | ||
| 988 | 990 | headers?: StringMap | null; |
| 989 | 991 | mcpDiscoveryElicitation?: McpDiscoveryElicitationContext; |
| 990 | 992 | resolveSecretMaterial: ResolveSecretMaterial; |
| 991 | −}): Effect.Effect<McpSourceConnectResult, Error, never> => | |
| 993 | +}): Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices> => | |
| 992 | 994 | Effect.gen(function* () { |
| 993 | 995 | const normalizedEndpoint = normalizeEndpoint(input.endpoint); |
| 994 | 996 | const existing = yield* ( |
| @@ -1200,13 +1202,13 @@ const connectMcpSourceInternal = (input: { · 205 unmodified lines | ||
| 1200 | 1202 | }); |
| 1201 | 1203 | |
| 1202 | 1204 | const addExecutorHttpSource = (input: { |
| 1203 | − rows: SqlControlPlaneRows; | |
| 1205 | + rows: ControlPlaneStoreShape; | |
| 1204 | 1206 | sourceInput: Extract<ExecutorAddSourceInput, { kind: "openapi" | "graphql" }>; |
| 1205 | 1207 | storeSecretMaterial: StoreSecretMaterial; |
| 1206 | 1208 | resolveSecretMaterial: ResolveSecretMaterial; |
| 1207 | 1209 | getLocalServerBaseUrl?: () => string | undefined; |
| 1208 | 1210 | baseUrl?: string | null; |
| 1209 | −}): Effect.Effect<ExecutorSourceAddResult, Error, never> => | |
| 1211 | +}): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> => | |
| 1210 | 1212 | Effect.gen(function* () { |
| 1211 | 1213 | const normalizedEndpoint = normalizeEndpoint(input.sourceInput.endpoint); |
| 1212 | 1214 | const normalizedSpecUrl = input.sourceInput.kind === "openapi" |
| @@ -1403,13 +1405,13 @@ const addExecutorHttpSource = (input: { · 190 unmodified lines | ||
| 1403 | 1405 | }); |
| 1404 | 1406 | |
| 1405 | 1407 | const addExecutorGoogleDiscoverySource = (input: { |
| 1406 | − rows: SqlControlPlaneRows; | |
| 1408 | + rows: ControlPlaneStoreShape; | |
| 1407 | 1409 | sourceInput: Extract<ExecutorAddSourceInput, { kind: "google_discovery" }>; |
| 1408 | 1410 | storeSecretMaterial: StoreSecretMaterial; |
| 1409 | 1411 | resolveSecretMaterial: ResolveSecretMaterial; |
| 1410 | 1412 | getLocalServerBaseUrl?: () => string | undefined; |
| 1411 | 1413 | baseUrl?: string | null; |
| 1412 | −}): Effect.Effect<ExecutorSourceAddResult, Error, never> => | |
| 1414 | +}): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> => | |
| 1413 | 1415 | Effect.gen(function* () { |
| 1414 | 1416 | const normalizedService = input.sourceInput.service.trim(); |
| 1415 | 1417 | const normalizedVersion = input.sourceInput.version.trim(); |
| @@ -1627,7 +1629,7 @@ type RuntimeSourceAuthServiceShape = { · 211 unmodified lines | ||
| 1627 | 1629 | workspaceId: WorkspaceId; |
| 1628 | 1630 | sourceId: Source["id"]; |
| 1629 | 1631 | actorAccountId?: AccountId | null; |
| 1630 | − }) => Effect.Effect<Source, Error, never>; | |
| 1632 | + }) => Effect.Effect<Source, Error, WorkspaceStorageServices>; | |
| 1631 | 1633 | getLocalServerBaseUrl: () => string | null; |
| 1632 | 1634 | storeSecretMaterial: (input: { |
| 1633 | 1635 | purpose: SecretMaterialPurpose; |
| @@ -1639,10 +1641,10 @@ type RuntimeSourceAuthServiceShape = { · 5 unmodified lines | ||
| 1639 | 1641 | mcpDiscoveryElicitation?: McpDiscoveryElicitationContext; |
| 1640 | 1642 | baseUrl?: string | null; |
| 1641 | 1643 | }, |
| 1642 | − ) => Effect.Effect<ExecutorSourceAddResult, Error, never>; | |
| 1644 | + ) => Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices>; | |
| 1643 | 1645 | connectMcpSource: ( |
| 1644 | 1646 | input: ConnectMcpSourceInput, |
| 1645 | − ) => Effect.Effect<McpSourceConnectResult, Error, never>; | |
| 1647 | + ) => Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices>; | |
| 1646 | 1648 | startSourceOAuthSession: ( |
| 1647 | 1649 | input: StartSourceOAuthSessionInput, |
| 1648 | 1650 | ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>; |
| @@ -1651,7 +1653,7 @@ type RuntimeSourceAuthServiceShape = { · 2 unmodified lines | ||
| 1651 | 1653 | code?: string | null; |
| 1652 | 1654 | error?: string | null; |
| 1653 | 1655 | errorDescription?: string | null; |
| 1654 | − }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>; | |
| 1656 | + }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, WorkspaceStorageServices>; | |
| 1655 | 1657 | completeSourceCredentialSetup: (input: { |
| 1656 | 1658 | workspaceId: WorkspaceId; |
| 1657 | 1659 | sourceId: Source["id"]; |
| @@ -1660,11 +1662,11 @@ type RuntimeSourceAuthServiceShape = { · 2 unmodified lines | ||
| 1660 | 1662 | code?: string | null; |
| 1661 | 1663 | error?: string | null; |
| 1662 | 1664 | errorDescription?: string | null; |
| 1663 | − }) => Effect.Effect<Source, Error, never>; | |
| 1665 | + }) => Effect.Effect<Source, Error, WorkspaceStorageServices>; | |
| 1664 | 1666 | }; |
| 1665 | 1667 | |
| 1666 | 1668 | export const createRuntimeSourceAuthService = (input: { |
| 1667 | − rows: SqlControlPlaneRows; | |
| 1669 | + rows: ControlPlaneStoreShape; | |
| 1668 | 1670 | liveExecutionManager: LiveExecutionManager; |
| 1669 | 1671 | getLocalServerBaseUrl?: () => string | undefined; |
| 1670 | 1672 | localConfig?: LocalExecutorConfig | null; |
| @@ -1681,11 +1683,11 @@ export const createRuntimeSourceAuthService = (input: { · 10 unmodified lines | ||
| 1681 | 1683 | }); |
| 1682 | 1684 | const mirrorLocalSourceResult = ( |
| 1683 | 1685 | result: ExecutorSourceAddResult, |
| 1684 | − ): Effect.Effect<ExecutorSourceAddResult, Error, never> => | |
| 1686 | + ): Effect.Effect<ExecutorSourceAddResult, Error, WorkspaceStorageServices> => | |
| 1685 | 1687 | Effect.succeed(result); |
| 1686 | 1688 | const mirrorLocalMcpSourceResult = ( |
| 1687 | 1689 | result: McpSourceConnectResult, |
| 1688 | − ): Effect.Effect<McpSourceConnectResult, Error, never> => | |
| 1690 | + ): Effect.Effect<McpSourceConnectResult, Error, WorkspaceStorageServices> => | |
| 1689 | 1691 | Effect.succeed(result); |
| 1690 | 1692 | const provideLocalWorkspace = <A, E, R>(effect: Effect.Effect<A, E, R>) => |
| 1691 | 1693 | input.localWorkspaceState |
| @@ -2250,7 +2252,7 @@ export const RuntimeSourceAuthServiceLive = (input: { · 558 unmodified lines | ||
| 2250 | 2252 | Layer.effect( |
| 2251 | 2253 | RuntimeSourceAuthServiceTag, |
| 2252 | 2254 | Effect.gen(function* () { |
| 2253 | − const rows = yield* SqlControlPlaneRowsService; | |
| 2255 | + const rows = yield* ControlPlaneStore; | |
| 2254 | 2256 | const liveExecutionManager = yield* LiveExecutionManagerService; |
| 2255 | 2257 | |
| 2256 | 2258 | return createRuntimeSourceAuthService({ |
| @@ -129,7 +129,7 @@ describe("source-definitions", () => { · 128 unmodified lines | ||
| 129 | 129 | headerName: "Authorization", |
| 130 | 130 | prefix: "Bearer ", |
| 131 | 131 | token: { |
| 132 | − providerId: "postgres", | |
| 132 | + providerId: "local", | |
| 133 | 133 | handle: "sec_token", |
| 134 | 134 | }, |
| 135 | 135 | }, |
| @@ -166,7 +166,7 @@ describe("source-definitions", () => { · 30 unmodified lines | ||
| 166 | 166 | headerName: " ", |
| 167 | 167 | prefix: undefined, |
| 168 | 168 | accessToken: { |
| 169 | − providerId: " postgres ", | |
| 169 | + providerId: " local ", | |
| 170 | 170 | handle: " sec_access ", |
| 171 | 171 | }, |
| 172 | 172 | refreshToken: null, |
| @@ -180,7 +180,7 @@ describe("source-definitions", () => { · 7 unmodified lines | ||
| 180 | 180 | headerName: "Authorization", |
| 181 | 181 | prefix: "Bearer ", |
| 182 | 182 | accessToken: { |
| 183 | − providerId: "postgres", | |
| 183 | + providerId: "local", | |
| 184 | 184 | handle: "sec_access", |
| 185 | 185 | }, |
| 186 | 186 | refreshToken: null, |
| @@ -222,11 +222,11 @@ describe("source-definitions", () => { · 35 unmodified lines | ||
| 222 | 222 | headerName: "Authorization", |
| 223 | 223 | prefix: "Bearer ", |
| 224 | 224 | accessToken: { |
| 225 | − providerId: "postgres", | |
| 225 | + providerId: "local", | |
| 226 | 226 | handle: "sec_access", |
| 227 | 227 | }, |
| 228 | 228 | refreshToken: { |
| 229 | − providerId: "postgres", | |
| 229 | + providerId: "local", | |
| 230 | 230 | handle: " ", |
| 231 | 231 | }, |
| 232 | 232 | } as never, |
| @@ -319,7 +319,7 @@ describe("source-definitions", () => { · 86 unmodified lines | ||
| 319 | 319 | headerName: " ", |
| 320 | 320 | prefix: "Token ", |
| 321 | 321 | token: { |
| 322 | − providerId: " postgres ", | |
| 322 | + providerId: " local ", | |
| 323 | 323 | handle: " sec_token ", |
| 324 | 324 | }, |
| 325 | 325 | } as never, |
| @@ -332,7 +332,7 @@ describe("source-definitions", () => { · 6 unmodified lines | ||
| 332 | 332 | headerName: "Authorization", |
| 333 | 333 | prefix: "Token ", |
| 334 | 334 | token: { |
| 335 | − providerId: "postgres", | |
| 335 | + providerId: "local", | |
| 336 | 336 | handle: "sec_token", |
| 337 | 337 | }, |
| 338 | 338 | }); |
| @@ -353,7 +353,7 @@ describe("source-definitions", () => { · 14 unmodified lines | ||
| 353 | 353 | headerName: "Authorization", |
| 354 | 354 | prefix: "Bearer ", |
| 355 | 355 | token: { |
| 356 | − providerId: "postgres", | |
| 356 | + providerId: "local", | |
| 357 | 357 | handle: "sec_bearer", |
| 358 | 358 | }, |
| 359 | 359 | }, |
| @@ -397,11 +397,11 @@ describe("source-definitions", () => { · 37 unmodified lines | ||
| 397 | 397 | headerName: "Authorization", |
| 398 | 398 | prefix: "Bearer ", |
| 399 | 399 | accessToken: { |
| 400 | − providerId: "postgres", | |
| 400 | + providerId: "local", | |
| 401 | 401 | handle: "sec_access", |
| 402 | 402 | }, |
| 403 | 403 | refreshToken: { |
| 404 | − providerId: "postgres", | |
| 404 | + providerId: "local", | |
| 405 | 405 | handle: "sec_refresh", |
| 406 | 406 | }, |
| 407 | 407 | }, |
| @@ -416,7 +416,7 @@ describe("source-definitions", () => { · 8 unmodified lines | ||
| 416 | 416 | headerName: "Authorization", |
| 417 | 417 | prefix: "Bearer ", |
| 418 | 418 | accessToken: { |
| 419 | − providerId: "postgres", | |
| 419 | + providerId: "local", | |
| 420 | 420 | handle: "sec_access", |
| 421 | 421 | }, |
| 422 | 422 | refreshToken: null, |
| @@ -1,344 +0,0 @@ | ||
| 1 | −import { mkdtempSync } from "node:fs"; | |
| 2 | −import { tmpdir } from "node:os"; | |
| 3 | −import { join } from "node:path"; | |
| 4 | − | |
| 5 | −import { describe, expect, it } from "vitest"; | |
| 6 | −import * as Effect from "effect/Effect"; | |
| 7 | − | |
| 8 | −import { | |
| 9 | − AccountIdSchema, | |
| 10 | − type LocalConfigSource, | |
| 11 | − SourceIdSchema, | |
| 12 | − SourceRecipeRevisionIdSchema, | |
| 13 | − SourceRecipeSchemaBundleIdSchema, | |
| 14 | − WorkspaceIdSchema, | |
| 15 | − type Source, | |
| 16 | − type StoredSourceRecipeOperationRecord, | |
| 17 | −} from "#schema"; | |
| 18 | −import { | |
| 19 | − createSqlControlPlanePersistence, | |
| 20 | − type SqlControlPlanePersistence, | |
| 21 | −} from "#persistence"; | |
| 22 | − | |
| 23 | −import { | |
| 24 | − loadLocalExecutorConfig, | |
| 25 | − resolveLocalWorkspaceContext, | |
| 26 | − writeProjectLocalExecutorConfig, | |
| 27 | −} from "./local-config"; | |
| 28 | −import { | |
| 29 | − buildLocalSourceArtifact, | |
| 30 | − writeLocalSourceArtifact, | |
| 31 | −} from "./local-source-artifacts"; | |
| 32 | −import { | |
| 33 | − getSourceInspection, | |
| 34 | − getSourceInspectionSchemaBundle, | |
| 35 | − getSourceInspectionToolDetail, | |
| 36 | −} from "./source-inspection"; | |
| 37 | −import { | |
| 38 | − RuntimeLocalWorkspaceService, | |
| 39 | − type RuntimeLocalWorkspaceState, | |
| 40 | −} from "./local-runtime-context"; | |
| 41 | −import { | |
| 42 | − writeLocalWorkspaceState, | |
| 43 | −} from "./local-workspace-state"; | |
| 44 | −import { ControlPlaneStore } from "./store"; | |
| 45 | − | |
| 46 | −const makePersistence = () => | |
| 47 | − Effect.runPromise( | |
| 48 | − createSqlControlPlanePersistence({ | |
| 49 | − localDataDir: ":memory:", | |
| 50 | − }), | |
| 51 | − ); | |
| 52 | − | |
| 53 | −const makeRuntimeLocalWorkspaceState = ( | |
| 54 | − workspaceId: ReturnType<typeof WorkspaceIdSchema.make>, | |
| 55 | − accountId: ReturnType<typeof AccountIdSchema.make>, | |
| 56 | −) => | |
| 57 | − Effect.runPromise( | |
| 58 | − Effect.gen(function* () { | |
| 59 | − const workspaceRoot = mkdtempSync( | |
| 60 | − join(tmpdir(), "executor-source-inspection-"), | |
| 61 | − ); | |
| 62 | − const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 63 | − const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 64 | − | |
| 65 | − return { | |
| 66 | − context, | |
| 67 | − installation: { | |
| 68 | − workspaceId, | |
| 69 | − accountId, | |
| 70 | − }, | |
| 71 | − loadedConfig, | |
| 72 | − } satisfies RuntimeLocalWorkspaceState; | |
| 73 | − }), | |
| 74 | − ); | |
| 75 | − | |
| 76 | −const makeSource = (input: { | |
| 77 | − workspaceId: Source["workspaceId"]; | |
| 78 | − sourceId: Source["id"]; | |
| 79 | −}): Source => ({ | |
| 80 | − id: input.sourceId, | |
| 81 | − workspaceId: input.workspaceId, | |
| 82 | − name: "Cloudflare API", | |
| 83 | − kind: "openapi", | |
| 84 | − endpoint: "https://api.cloudflare.com/client/v4", | |
| 85 | − status: "connected", | |
| 86 | − enabled: true, | |
| 87 | − namespace: "cloudflare.api", | |
| 88 | − bindingVersion: 1, | |
| 89 | − binding: { | |
| 90 | − specUrl: "https://example.com/openapi.json", | |
| 91 | − defaultHeaders: null, | |
| 92 | − }, | |
| 93 | − importAuthPolicy: "reuse_runtime", | |
| 94 | − importAuth: { kind: "none" }, | |
| 95 | − auth: { kind: "none" }, | |
| 96 | − sourceHash: null, | |
| 97 | − lastError: null, | |
| 98 | − createdAt: 1000, | |
| 99 | − updatedAt: 1000, | |
| 100 | −}); | |
| 101 | − | |
| 102 | −const makeOperation = ( | |
| 103 | − overrides: Partial<StoredSourceRecipeOperationRecord> = {}, | |
| 104 | −): StoredSourceRecipeOperationRecord => ({ | |
| 105 | − id: "src_recipe_op_inspection", | |
| 106 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_inspection"), | |
| 107 | − operationKey: "zones.listZones", | |
| 108 | − transportKind: "http", | |
| 109 | − toolId: "zones.listZones", | |
| 110 | − title: "List zones", | |
| 111 | − description: "List Cloudflare zones", | |
| 112 | − operationKind: "read", | |
| 113 | − searchText: "zones list cloudflare", | |
| 114 | − inputSchemaJson: JSON.stringify({ | |
| 115 | − type: "object", | |
| 116 | − properties: { | |
| 117 | − page: { type: "number" }, | |
| 118 | − }, | |
| 119 | − }), | |
| 120 | − outputSchemaJson: JSON.stringify({ | |
| 121 | − type: "object", | |
| 122 | − properties: { | |
| 123 | − result: { | |
| 124 | − type: "array", | |
| 125 | − items: { type: "string" }, | |
| 126 | − }, | |
| 127 | − }, | |
| 128 | − }), | |
| 129 | − providerKind: "openapi", | |
| 130 | − providerDataJson: JSON.stringify({ | |
| 131 | − kind: "openapi", | |
| 132 | − toolId: "zones.listZones", | |
| 133 | − rawToolId: "zones_listZones", | |
| 134 | − operationId: "zones.listZones", | |
| 135 | − group: "zones", | |
| 136 | − leaf: "listZones", | |
| 137 | − tags: ["zones"], | |
| 138 | − method: "get", | |
| 139 | − path: "/zones", | |
| 140 | − operationHash: "hash", | |
| 141 | − invocation: { | |
| 142 | − method: "get", | |
| 143 | − pathTemplate: "/zones", | |
| 144 | − parameters: [], | |
| 145 | − requestBody: null, | |
| 146 | − }, | |
| 147 | − }), | |
| 148 | − createdAt: 1000, | |
| 149 | − updatedAt: 1000, | |
| 150 | − ...overrides, | |
| 151 | −}); | |
| 152 | − | |
| 153 | −const configSourceFromSource = (source: Source): LocalConfigSource => ({ | |
| 154 | − kind: "openapi", | |
| 155 | − ...(source.name !== source.id ? { name: source.name } : {}), | |
| 156 | − ...(source.namespace && source.namespace !== source.id | |
| 157 | − ? { namespace: source.namespace } | |
| 158 | − : {}), | |
| 159 | − connection: { | |
| 160 | − endpoint: source.endpoint, | |
| 161 | − }, | |
| 162 | − binding: { | |
| 163 | − specUrl: | |
| 164 | − typeof source.binding.specUrl === "string" | |
| 165 | − ? source.binding.specUrl | |
| 166 | − : "https://example.com/openapi.json", | |
| 167 | − defaultHeaders: | |
| 168 | − (source.binding.defaultHeaders as Record<string, string> | null | undefined) | |
| 169 | − ?? null, | |
| 170 | − }, | |
| 171 | −}); | |
| 172 | − | |
| 173 | −describe("source-inspection", () => { | |
| 174 | − it("returns a lightweight inspection bundle and loads rich detail on demand", async () => { | |
| 175 | − const persistence = await makePersistence(); | |
| 176 | − try { | |
| 177 | − const workspaceId = WorkspaceIdSchema.make("ws_source_inspection"); | |
| 178 | − const accountId = AccountIdSchema.make("acc_source_inspection"); | |
| 179 | − const sourceId = SourceIdSchema.make("src_source_inspection"); | |
| 180 | − const recipeRevisionId = | |
| 181 | − SourceRecipeRevisionIdSchema.make("src_recipe_rev_materialization"); | |
| 182 | − const runtimeLocalWorkspace = await makeRuntimeLocalWorkspaceState( | |
| 183 | − workspaceId, | |
| 184 | − accountId, | |
| 185 | − ); | |
| 186 | − const hugeDocument = JSON.stringify({ | |
| 187 | − openapi: "3.0.3", | |
| 188 | − info: { | |
| 189 | − title: "Cloudflare API", | |
| 190 | − version: "1.0.0", | |
| 191 | − }, | |
| 192 | − paths: Object.fromEntries( | |
| 193 | − Array.from({ length: 256 }, (_, index) => [ | |
| 194 | − `/zones/${index}`, | |
| 195 | − { | |
| 196 | − get: { | |
| 197 | − operationId: `zones.listZones${index}`, | |
| 198 | − responses: { 200: { description: "ok" } }, | |
| 199 | − }, | |
| 200 | − }, | |
| 201 | − ]), | |
| 202 | − ), | |
| 203 | − }); | |
| 204 | − | |
| 205 | − const source = makeSource({ | |
| 206 | − workspaceId, | |
| 207 | − sourceId, | |
| 208 | − }); | |
| 209 | − await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 210 | − context: runtimeLocalWorkspace.context, | |
| 211 | − config: { | |
| 212 | − sources: { | |
| 213 | − [sourceId]: configSourceFromSource(source), | |
| 214 | − }, | |
| 215 | − }, | |
| 216 | − })); | |
| 217 | − await Effect.runPromise(writeLocalSourceArtifact({ | |
| 218 | − context: runtimeLocalWorkspace.context, | |
| 219 | − sourceId, | |
| 220 | − artifact: buildLocalSourceArtifact({ | |
| 221 | − source: { | |
| 222 | − ...source, | |
| 223 | − sourceHash: "manifest_hash", | |
| 224 | − }, | |
| 225 | − materialization: { | |
| 226 | − manifestJson: JSON.stringify({ | |
| 227 | − sourceHash: "manifest_hash", | |
| 228 | − }), | |
| 229 | − manifestHash: "manifest_hash", | |
| 230 | − sourceHash: "manifest_hash", | |
| 231 | − documents: [{ | |
| 232 | − id: "src_recipe_doc_inspection", | |
| 233 | − recipeRevisionId, | |
| 234 | − documentKind: "openapi", | |
| 235 | − documentKey: "https://example.com/openapi.json", | |
| 236 | − contentText: hugeDocument, | |
| 237 | − contentHash: "doc_hash", | |
| 238 | − fetchedAt: 1000, | |
| 239 | − createdAt: 1000, | |
| 240 | − updatedAt: 1000, | |
| 241 | − }], | |
| 242 | − schemaBundles: [{ | |
| 243 | − id: SourceRecipeSchemaBundleIdSchema.make( | |
| 244 | − "src_recipe_bundle_inspection", | |
| 245 | − ), | |
| 246 | − recipeRevisionId, | |
| 247 | − bundleKind: "json_schema_ref_map", | |
| 248 | − refsJson: JSON.stringify({ | |
| 249 | − "#/components/schemas/Pagination": { | |
| 250 | − type: "object", | |
| 251 | − properties: { | |
| 252 | − page: { type: "number" }, | |
| 253 | − }, | |
| 254 | − }, | |
| 255 | − }), | |
| 256 | − contentHash: "bundle_hash", | |
| 257 | − createdAt: 1000, | |
| 258 | − updatedAt: 1000, | |
| 259 | − }], | |
| 260 | − operations: [makeOperation({ | |
| 261 | − recipeRevisionId, | |
| 262 | − })], | |
| 263 | − }, | |
| 264 | − }), | |
| 265 | − })); | |
| 266 | − await Effect.runPromise(writeLocalWorkspaceState({ | |
| 267 | − context: runtimeLocalWorkspace.context, | |
| 268 | − state: { | |
| 269 | − version: 1, | |
| 270 | − sources: { | |
| 271 | − [sourceId]: { | |
| 272 | − status: "connected", | |
| 273 | − lastError: null, | |
| 274 | − sourceHash: "manifest_hash", | |
| 275 | − createdAt: 1000, | |
| 276 | − updatedAt: 1000, | |
| 277 | − }, | |
| 278 | − }, | |
| 279 | − policies: {}, | |
| 280 | − }, | |
| 281 | − })); | |
| 282 | − | |
| 283 | − const inspection = await Effect.runPromise( | |
| 284 | − getSourceInspection({ | |
| 285 | − workspaceId, | |
| 286 | − sourceId, | |
| 287 | − }).pipe( | |
| 288 | − Effect.provideService(ControlPlaneStore, persistence.rows), | |
| 289 | − Effect.provideService( | |
| 290 | − RuntimeLocalWorkspaceService, | |
| 291 | − runtimeLocalWorkspace, | |
| 292 | − ), | |
| 293 | − ), | |
| 294 | − ); | |
| 295 | − | |
| 296 | − expect(inspection.toolCount).toBe(1); | |
| 297 | − expect(inspection.tools[0]?.path).toBe("cloudflare.api.zones.listZones"); | |
| 298 | − expect("manifestJson" in inspection).toBe(false); | |
| 299 | − expect("rawDocumentText" in inspection).toBe(false); | |
| 300 | − expect("definitionsJson" in inspection).toBe(false); | |
| 301 | − | |
| 302 | − const detail = await Effect.runPromise( | |
| 303 | − getSourceInspectionToolDetail({ | |
| 304 | − workspaceId, | |
| 305 | − sourceId, | |
| 306 | − toolPath: "cloudflare.api.zones.listZones", | |
| 307 | − }).pipe( | |
| 308 | − Effect.provideService(ControlPlaneStore, persistence.rows), | |
| 309 | − Effect.provideService( | |
| 310 | − RuntimeLocalWorkspaceService, | |
| 311 | − runtimeLocalWorkspace, | |
| 312 | − ), | |
| 313 | − ), | |
| 314 | − ); | |
| 315 | − | |
| 316 | − expect(detail.summary.method).toBe("get"); | |
| 317 | − expect(detail.summary.inputType).toContain("page"); | |
| 318 | − expect(detail.summary.outputType).toContain("result"); | |
| 319 | − expect(detail.providerDataJson).toContain("/zones"); | |
| 320 | − expect(detail.inputSchemaJson).toContain("\"page\""); | |
| 321 | − expect(detail.outputSchemaJson).toContain("\"result\""); | |
| 322 | − expect(detail.schemaBundleId).toBe("src_recipe_bundle_inspection"); | |
| 323 | − | |
| 324 | − const schemaBundle = await Effect.runPromise( | |
| 325 | − getSourceInspectionSchemaBundle({ | |
| 326 | − workspaceId, | |
| 327 | − sourceId, | |
| 328 | − schemaBundleId: "src_recipe_bundle_inspection", | |
| 329 | − }).pipe( | |
| 330 | − Effect.provideService(ControlPlaneStore, persistence.rows), | |
| 331 | − Effect.provideService( | |
| 332 | − RuntimeLocalWorkspaceService, | |
| 333 | − runtimeLocalWorkspace, | |
| 334 | − ), | |
| 335 | − ), | |
| 336 | − ); | |
| 337 | − | |
| 338 | − expect(schemaBundle.kind).toBe("json_schema_ref_map"); | |
| 339 | − expect(schemaBundle.refsJson).toContain("Pagination"); | |
| 340 | − } finally { | |
| 341 | − await persistence.close(); | |
| 342 | − } | |
| 343 | − }); | |
| 344 | −}); | |
| @@ -1,4 +1,3 @@ | ||
| 1 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 2 | 1 | import type { |
| 3 | 2 | AccountId, |
| 4 | 3 | Source, |
| @@ -6,16 +5,16 @@ import type { · 1 unmodified line | ||
| 6 | 5 | } from "#schema"; |
| 7 | 6 | import * as Effect from "effect/Effect"; |
| 8 | 7 | |
| 9 | −import { | |
| 10 | − buildLocalSourceArtifact, | |
| 11 | − writeLocalSourceArtifact, | |
| 12 | −} from "./local-source-artifacts"; | |
| 8 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 13 | 9 | import { |
| 14 | 10 | requireRuntimeLocalWorkspace, |
| 15 | 11 | } from "./local-runtime-context"; |
| 16 | 12 | import { |
| 17 | − loadLocalWorkspaceState, | |
| 18 | − writeLocalWorkspaceState, | |
| 13 | + type WorkspaceStorageServices, | |
| 14 | + SourceArtifactStore, | |
| 15 | + WorkspaceStateStore, | |
| 16 | +} from "./local-storage"; | |
| 17 | +import { | |
| 19 | 18 | type LocalWorkspaceState, |
| 20 | 19 | } from "./local-workspace-state"; |
| 21 | 20 | import { resolveSourceAuthMaterial } from "./source-auth-material"; |
| @@ -24,7 +23,6 @@ import { · 2 unmodified lines | ||
| 24 | 23 | } from "./source-adapters"; |
| 25 | 24 | import { |
| 26 | 25 | materializationFromMcpManifestEntries, |
| 27 | − persistMcpRecipeRevisionFromManifestEntries, | |
| 28 | 26 | } from "./source-adapters/mcp"; |
| 29 | 27 | import type { |
| 30 | 28 | ResolveSecretMaterial as ResolveSourceSecretMaterial, |
| @@ -36,16 +34,20 @@ const shouldIndexSource = (source: Source): boolean => · 5 unmodified lines | ||
| 36 | 34 | && getSourceAdapterForSource(source).family !== "internal"; |
| 37 | 35 | |
| 38 | 36 | export const syncSourceMaterialization = (input: { |
| 39 | − rows: SqlControlPlaneRows; | |
| 37 | + rows: ControlPlaneStoreShape; | |
| 40 | 38 | source: Source; |
| 41 | 39 | actorAccountId?: AccountId | null; |
| 42 | 40 | resolveSecretMaterial: ResolveSourceSecretMaterial; |
| 43 | −}): Effect.Effect<void, Error, never> => | |
| 41 | +}): Effect.Effect<void, Error, WorkspaceStorageServices> => | |
| 44 | 42 | Effect.gen(function* () { |
| 45 | 43 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(input.source.workspaceId); |
| 44 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 45 | + const sourceArtifactStore = yield* SourceArtifactStore; | |
| 46 | 46 | |
| 47 | 47 | if (!shouldIndexSource(input.source)) { |
| 48 | − const state = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 48 | + const state = yield* workspaceStateStore.load( | |
| 49 | + runtimeLocalWorkspace.context, | |
| 50 | + ); | |
| 49 | 51 | const existingSourceState = state.sources[input.source.id]; |
| 50 | 52 | const nextState: LocalWorkspaceState = { |
| 51 | 53 | ...state, |
| @@ -60,7 +62,7 @@ export const syncSourceMaterialization = (input: { · 8 unmodified lines | ||
| 60 | 62 | }, |
| 61 | 63 | }, |
| 62 | 64 | }; |
| 63 | − yield* writeLocalWorkspaceState({ | |
| 65 | + yield* workspaceStateStore.write({ | |
| 64 | 66 | context: runtimeLocalWorkspace.context, |
| 65 | 67 | state: nextState, |
| 66 | 68 | }); |
| @@ -80,16 +82,18 @@ export const syncSourceMaterialization = (input: { · 13 unmodified lines | ||
| 80 | 82 | resolveSecretMaterial: input.resolveSecretMaterial, |
| 81 | 83 | }), |
| 82 | 84 | }); |
| 83 | − yield* writeLocalSourceArtifact({ | |
| 85 | + yield* sourceArtifactStore.write({ | |
| 84 | 86 | context: runtimeLocalWorkspace.context, |
| 85 | 87 | sourceId: input.source.id, |
| 86 | − artifact: buildLocalSourceArtifact({ | |
| 88 | + artifact: sourceArtifactStore.build({ | |
| 87 | 89 | source: input.source, |
| 88 | 90 | materialization, |
| 89 | 91 | }), |
| 90 | 92 | }); |
| 91 | 93 | |
| 92 | − const state = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 94 | + const state = yield* workspaceStateStore.load( | |
| 95 | + runtimeLocalWorkspace.context, | |
| 96 | + ); | |
| 93 | 97 | const existingSourceState = state.sources[input.source.id]; |
| 94 | 98 | const nextState: LocalWorkspaceState = { |
| 95 | 99 | ...state, |
| @@ -104,31 +108,30 @@ export const syncSourceMaterialization = (input: { · 8 unmodified lines | ||
| 104 | 108 | }, |
| 105 | 109 | }, |
| 106 | 110 | }; |
| 107 | − yield* writeLocalWorkspaceState({ | |
| 111 | + yield* workspaceStateStore.write({ | |
| 108 | 112 | context: runtimeLocalWorkspace.context, |
| 109 | 113 | state: nextState, |
| 110 | 114 | }); |
| 111 | 115 | }); |
| 112 | 116 | |
| 113 | 117 | export const persistMcpRecipeMaterializationFromManifest = (input: { |
| 114 | − rows: SqlControlPlaneRows; | |
| 118 | + rows: ControlPlaneStoreShape; | |
| 115 | 119 | source: Source; |
| 116 | − manifestEntries: Parameters< | |
| 117 | − typeof persistMcpRecipeRevisionFromManifestEntries | |
| 118 | − >[0]["manifestEntries"]; | |
| 119 | −}): Effect.Effect<void, Error, never> => | |
| 120 | + manifestEntries: Parameters<typeof materializationFromMcpManifestEntries>[0]["manifestEntries"]; | |
| 121 | +}): Effect.Effect<void, Error, WorkspaceStorageServices> => | |
| 120 | 122 | Effect.gen(function* () { |
| 121 | 123 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(input.source.workspaceId); |
| 124 | + const sourceArtifactStore = yield* SourceArtifactStore; | |
| 122 | 125 | const materialization = materializationFromMcpManifestEntries({ |
| 123 | 126 | recipeRevisionId: "src_recipe_rev_materialization" as never, |
| 124 | 127 | endpoint: input.source.endpoint, |
| 125 | 128 | manifestEntries: input.manifestEntries, |
| 126 | 129 | }); |
| 127 | 130 | |
| 128 | − yield* writeLocalSourceArtifact({ | |
| 131 | + yield* sourceArtifactStore.write({ | |
| 129 | 132 | context: runtimeLocalWorkspace.context, |
| 130 | 133 | sourceId: input.source.id, |
| 131 | − artifact: buildLocalSourceArtifact({ | |
| 134 | + artifact: sourceArtifactStore.build({ | |
| 132 | 135 | source: input.source, |
| 133 | 136 | materialization, |
| 134 | 137 | }), |
| @@ -1,22 +1,10 @@ | ||
| 1 | 1 | import { sha256Hex } from "@executor/codemode-core"; |
| 2 | 2 | |
| 3 | 3 | import type { |
| 4 | − Source, | |
| 5 | 4 | StoredSourceRecipeDocumentRecord, |
| 6 | 5 | StoredSourceRecipeOperationRecord, |
| 7 | 6 | StoredSourceRecipeSchemaBundleRecord, |
| 8 | − StoredSourceRecipeRevisionRecord, | |
| 9 | 7 | } from "#schema"; |
| 10 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 11 | −import * as Effect from "effect/Effect"; | |
| 12 | −import * as Option from "effect/Option"; | |
| 13 | − | |
| 14 | −type SourceBindingRecord = | |
| 15 | − Awaited< | |
| 16 | − ReturnType<SqlControlPlaneRows["sources"]["getByWorkspaceAndId"]> | |
| 17 | − > extends Effect.Effect<Option.Option<infer T>, unknown, never> | |
| 18 | − ? T | |
| 19 | − : never; | |
| 20 | 8 | |
| 21 | 9 | export const normalizeSearchText = ( |
| 22 | 10 | ...parts: ReadonlyArray<string | null | undefined> |
| @@ -27,32 +15,7 @@ export const normalizeSearchText = ( · 4 unmodified lines | ||
| 27 | 15 | .join(" ") |
| 28 | 16 | .toLowerCase(); |
| 29 | 17 | |
| 30 | −export const contentHash = (value: string): string => | |
| 31 | − sha256Hex(value); | |
| 32 | − | |
| 33 | −export const loadSourceBindingRecord = (input: { | |
| 34 | − rows: SqlControlPlaneRows; | |
| 35 | − source: Source; | |
| 36 | −}): Effect.Effect<SourceBindingRecord, Error, never> => | |
| 37 | − Effect.gen(function* () { | |
| 38 | − const sourceRecord = yield* input.rows.sources | |
| 39 | − .getByWorkspaceAndId(input.source.workspaceId, input.source.id) | |
| 40 | − .pipe( | |
| 41 | − Effect.mapError((cause) => | |
| 42 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 43 | − ), | |
| 44 | − ); | |
| 45 | − | |
| 46 | − if (Option.isNone(sourceRecord)) { | |
| 47 | − return yield* Effect.fail( | |
| 48 | − new Error( | |
| 49 | − `Source disappeared while syncing recipe data for ${input.source.id}`, | |
| 50 | − ), | |
| 51 | − ); | |
| 52 | − } | |
| 53 | − | |
| 54 | − return sourceRecord.value; | |
| 55 | − }); | |
| 18 | +export const contentHash = (value: string): string => sha256Hex(value); | |
| 56 | 19 | |
| 57 | 20 | export type SourceRecipeMaterialization = { |
| 58 | 21 | manifestJson: string | null; |
| @@ -62,313 +25,3 @@ export type SourceRecipeMaterialization = { · 3 unmodified lines | ||
| 62 | 25 | schemaBundles: readonly StoredSourceRecipeSchemaBundleRecord[]; |
| 63 | 26 | operations: readonly StoredSourceRecipeOperationRecord[]; |
| 64 | 27 | }; |
| 65 | − | |
| 66 | −const canonicalMaterializationHash = (input: SourceRecipeMaterialization): string => { | |
| 67 | − const documents = [...input.documents] | |
| 68 | − .map((document) => ({ | |
| 69 | − documentKind: document.documentKind, | |
| 70 | − documentKey: document.documentKey, | |
| 71 | − contentHash: document.contentHash, | |
| 72 | − })) | |
| 73 | − .sort((left, right) => | |
| 74 | − left.documentKind.localeCompare(right.documentKind) | |
| 75 | − || left.documentKey.localeCompare(right.documentKey) | |
| 76 | − || left.contentHash.localeCompare(right.contentHash) | |
| 77 | − ); | |
| 78 | − const schemaBundles = [...input.schemaBundles] | |
| 79 | − .map((bundle) => ({ | |
| 80 | − bundleKind: bundle.bundleKind, | |
| 81 | − contentHash: bundle.contentHash, | |
| 82 | − })) | |
| 83 | − .sort((left, right) => | |
| 84 | − left.bundleKind.localeCompare(right.bundleKind) | |
| 85 | − || left.contentHash.localeCompare(right.contentHash) | |
| 86 | − ); | |
| 87 | − const operations = [...input.operations] | |
| 88 | − .map((operation) => ({ | |
| 89 | − operationKey: operation.operationKey, | |
| 90 | − transportKind: operation.transportKind, | |
| 91 | − toolId: operation.toolId, | |
| 92 | − title: operation.title, | |
| 93 | − description: operation.description, | |
| 94 | − operationKind: operation.operationKind, | |
| 95 | − searchText: operation.searchText, | |
| 96 | − inputSchemaJson: operation.inputSchemaJson, | |
| 97 | − outputSchemaJson: operation.outputSchemaJson, | |
| 98 | − providerKind: operation.providerKind, | |
| 99 | − providerDataJson: operation.providerDataJson, | |
| 100 | − })) | |
| 101 | − .sort((left, right) => left.operationKey.localeCompare(right.operationKey)); | |
| 102 | − | |
| 103 | − return contentHash(JSON.stringify({ | |
| 104 | − schemaVersion: 1, | |
| 105 | − manifestHash: input.manifestHash, | |
| 106 | − manifestJson: input.manifestJson, | |
| 107 | − documents, | |
| 108 | − schemaBundles, | |
| 109 | − operations, | |
| 110 | − })); | |
| 111 | −}; | |
| 112 | − | |
| 113 | −const replaceRevisionContent = (input: { | |
| 114 | − rows: SqlControlPlaneRows; | |
| 115 | − recipeRevisionId: StoredSourceRecipeRevisionRecord["id"]; | |
| 116 | − materializationHash: string; | |
| 117 | − materialization: SourceRecipeMaterialization; | |
| 118 | − updatedAt: number; | |
| 119 | −}) => | |
| 120 | − Effect.gen(function* () { | |
| 121 | − const boundDocuments = input.materialization.documents.map((document) => ({ | |
| 122 | − ...document, | |
| 123 | − recipeRevisionId: input.recipeRevisionId, | |
| 124 | − })); | |
| 125 | − const boundSchemaBundles = input.materialization.schemaBundles.map((bundle) => ({ | |
| 126 | − ...bundle, | |
| 127 | − recipeRevisionId: input.recipeRevisionId, | |
| 128 | − })); | |
| 129 | − const boundOperations = input.materialization.operations.map((operation) => ({ | |
| 130 | − ...operation, | |
| 131 | − recipeRevisionId: input.recipeRevisionId, | |
| 132 | − })); | |
| 133 | − | |
| 134 | − yield* input.rows.sourceRecipeRevisions | |
| 135 | − .update(input.recipeRevisionId, { | |
| 136 | − manifestJson: input.materialization.manifestJson, | |
| 137 | − manifestHash: input.materialization.manifestHash, | |
| 138 | − materializationHash: input.materializationHash, | |
| 139 | − updatedAt: input.updatedAt, | |
| 140 | − }) | |
| 141 | − .pipe( | |
| 142 | − Effect.mapError((cause) => | |
| 143 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 144 | − ), | |
| 145 | − ); | |
| 146 | − | |
| 147 | − yield* input.rows.sourceRecipeDocuments | |
| 148 | − .replaceForRevision({ | |
| 149 | − recipeRevisionId: input.recipeRevisionId, | |
| 150 | − documents: boundDocuments, | |
| 151 | − }) | |
| 152 | − .pipe( | |
| 153 | − Effect.mapError((cause) => | |
| 154 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 155 | − ), | |
| 156 | − ); | |
| 157 | − | |
| 158 | − yield* input.rows.sourceRecipeSchemaBundles | |
| 159 | − .replaceForRevision({ | |
| 160 | − recipeRevisionId: input.recipeRevisionId, | |
| 161 | − bundles: boundSchemaBundles, | |
| 162 | − }) | |
| 163 | − .pipe( | |
| 164 | − Effect.mapError((cause) => | |
| 165 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 166 | − ), | |
| 167 | − ); | |
| 168 | − | |
| 169 | − yield* input.rows.sourceRecipeOperations | |
| 170 | − .replaceForRevision({ | |
| 171 | − recipeRevisionId: input.recipeRevisionId, | |
| 172 | − operations: boundOperations, | |
| 173 | − }) | |
| 174 | − .pipe( | |
| 175 | − Effect.mapError((cause) => | |
| 176 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 177 | − ), | |
| 178 | − ); | |
| 179 | − }); | |
| 180 | − | |
| 181 | −const cleanupOrphanedRevision = (input: { | |
| 182 | − rows: SqlControlPlaneRows; | |
| 183 | − recipeRevisionId: StoredSourceRecipeRevisionRecord["id"]; | |
| 184 | −}) => | |
| 185 | − Effect.gen(function* () { | |
| 186 | − const referenceCount = yield* input.rows.sources.countByRecipeRevisionId(input.recipeRevisionId); | |
| 187 | − if (referenceCount > 0) { | |
| 188 | − return; | |
| 189 | − } | |
| 190 | − | |
| 191 | − yield* input.rows.sourceRecipeDocuments.removeByRevisionId(input.recipeRevisionId); | |
| 192 | − yield* input.rows.sourceRecipeSchemaBundles.removeByRevisionId(input.recipeRevisionId); | |
| 193 | − yield* input.rows.sourceRecipeOperations.removeByRevisionId(input.recipeRevisionId); | |
| 194 | − yield* input.rows.sourceRecipeRevisions.removeById(input.recipeRevisionId); | |
| 195 | − }); | |
| 196 | − | |
| 197 | −const updateRecipeLatestRevision = (input: { | |
| 198 | − rows: SqlControlPlaneRows; | |
| 199 | − recipeId: SourceBindingRecord["recipeId"]; | |
| 200 | − latestRevisionId: StoredSourceRecipeRevisionRecord["id"]; | |
| 201 | − updatedAt: number; | |
| 202 | −}) => | |
| 203 | − Effect.gen(function* () { | |
| 204 | − const recipe = yield* input.rows.sourceRecipes.getById(input.recipeId); | |
| 205 | − if (Option.isNone(recipe)) { | |
| 206 | − return yield* Effect.fail( | |
| 207 | − new Error(`Recipe disappeared while syncing recipe data for ${input.recipeId}`), | |
| 208 | − ); | |
| 209 | − } | |
| 210 | − | |
| 211 | − yield* input.rows.sourceRecipes.upsert({ | |
| 212 | − ...recipe.value, | |
| 213 | − latestRevisionId: input.latestRevisionId, | |
| 214 | − updatedAt: input.updatedAt, | |
| 215 | − }); | |
| 216 | − }); | |
| 217 | − | |
| 218 | −export const persistRecipeMaterialization = (input: { | |
| 219 | − rows: SqlControlPlaneRows; | |
| 220 | − source: Source; | |
| 221 | − materialization: SourceRecipeMaterialization; | |
| 222 | −}): Effect.Effect<void, Error, never> => | |
| 223 | − Effect.gen(function* () { | |
| 224 | − const sourceRecord = yield* loadSourceBindingRecord({ | |
| 225 | − rows: input.rows, | |
| 226 | − source: input.source, | |
| 227 | − }); | |
| 228 | − const now = Date.now(); | |
| 229 | − const materializationHash = canonicalMaterializationHash(input.materialization); | |
| 230 | − const currentRevision = yield* input.rows.sourceRecipeRevisions.getById( | |
| 231 | − sourceRecord.recipeRevisionId, | |
| 232 | − ); | |
| 233 | − | |
| 234 | − if (Option.isNone(currentRevision)) { | |
| 235 | − return yield* Effect.fail( | |
| 236 | − new Error( | |
| 237 | − `Recipe revision disappeared while syncing recipe data for ${input.source.id}`, | |
| 238 | − ), | |
| 239 | − ); | |
| 240 | − } | |
| 241 | − | |
| 242 | − const existingMaterializedRevision = yield* input.rows.sourceRecipeRevisions | |
| 243 | − .getByRecipeAndMaterializationHash({ | |
| 244 | − recipeId: sourceRecord.recipeId, | |
| 245 | − materializationHash, | |
| 246 | − }); | |
| 247 | − | |
| 248 | − if ( | |
| 249 | − Option.isSome(existingMaterializedRevision) | |
| 250 | − && existingMaterializedRevision.value.id !== currentRevision.value.id | |
| 251 | − ) { | |
| 252 | − const movedSource = yield* input.rows.sources.update( | |
| 253 | − input.source.workspaceId, | |
| 254 | − input.source.id, | |
| 255 | − { | |
| 256 | − recipeRevisionId: existingMaterializedRevision.value.id, | |
| 257 | − sourceHash: input.materialization.sourceHash, | |
| 258 | − updatedAt: now, | |
| 259 | − }, | |
| 260 | − ); | |
| 261 | − if (Option.isNone(movedSource)) { | |
| 262 | − return yield* Effect.fail( | |
| 263 | − new Error(`Source disappeared while updating recipe materialization for ${input.source.id}`), | |
| 264 | − ); | |
| 265 | − } | |
| 266 | − | |
| 267 | − yield* updateRecipeLatestRevision({ | |
| 268 | − rows: input.rows, | |
| 269 | − recipeId: sourceRecord.recipeId, | |
| 270 | − latestRevisionId: existingMaterializedRevision.value.id, | |
| 271 | − updatedAt: now, | |
| 272 | − }); | |
| 273 | − yield* cleanupOrphanedRevision({ | |
| 274 | − rows: input.rows, | |
| 275 | − recipeRevisionId: currentRevision.value.id, | |
| 276 | − }); | |
| 277 | − return; | |
| 278 | − } | |
| 279 | − | |
| 280 | − const needsFreshRevision = | |
| 281 | − currentRevision.value.materializationHash !== null | |
| 282 | − && currentRevision.value.materializationHash !== materializationHash; | |
| 283 | − | |
| 284 | − const nextRevisionNumber = needsFreshRevision | |
| 285 | − ? yield* input.rows.sourceRecipeRevisions.nextRevisionNumber(sourceRecord.recipeId) | |
| 286 | − : null; | |
| 287 | − | |
| 288 | − const targetRevision = needsFreshRevision | |
| 289 | − ? createFreshTargetRevision({ | |
| 290 | − currentRevision: currentRevision.value, | |
| 291 | − materializationHash, | |
| 292 | − now, | |
| 293 | − nextRevisionNumber: nextRevisionNumber!, | |
| 294 | − }) | |
| 295 | − : { | |
| 296 | − revision: currentRevision.value, | |
| 297 | − movedFromRevisionId: null as StoredSourceRecipeRevisionRecord["id"] | null, | |
| 298 | − }; | |
| 299 | − | |
| 300 | − if (needsFreshRevision) { | |
| 301 | − yield* input.rows.sourceRecipeRevisions.upsert(targetRevision.revision); | |
| 302 | − const movedSource = yield* input.rows.sources.update( | |
| 303 | − input.source.workspaceId, | |
| 304 | − input.source.id, | |
| 305 | − { | |
| 306 | − recipeRevisionId: targetRevision.revision.id, | |
| 307 | − sourceHash: input.materialization.sourceHash, | |
| 308 | − updatedAt: now, | |
| 309 | − }, | |
| 310 | − ); | |
| 311 | − if (Option.isNone(movedSource)) { | |
| 312 | − return yield* Effect.fail( | |
| 313 | − new Error(`Source disappeared while switching recipe materialization for ${input.source.id}`), | |
| 314 | − ); | |
| 315 | − } | |
| 316 | − } else { | |
| 317 | − const updatedSource = yield* input.rows.sources.update( | |
| 318 | − input.source.workspaceId, | |
| 319 | − input.source.id, | |
| 320 | − { | |
| 321 | − sourceHash: input.materialization.sourceHash, | |
| 322 | − updatedAt: now, | |
| 323 | − }, | |
| 324 | − ); | |
| 325 | − if (Option.isNone(updatedSource)) { | |
| 326 | − return yield* Effect.fail( | |
| 327 | − new Error(`Source disappeared while updating sync metadata for ${input.source.id}`), | |
| 328 | − ); | |
| 329 | − } | |
| 330 | − } | |
| 331 | − | |
| 332 | − yield* replaceRevisionContent({ | |
| 333 | − rows: input.rows, | |
| 334 | − recipeRevisionId: targetRevision.revision.id, | |
| 335 | − materializationHash, | |
| 336 | − materialization: input.materialization, | |
| 337 | − updatedAt: now, | |
| 338 | − }); | |
| 339 | − yield* updateRecipeLatestRevision({ | |
| 340 | − rows: input.rows, | |
| 341 | − recipeId: sourceRecord.recipeId, | |
| 342 | − latestRevisionId: targetRevision.revision.id, | |
| 343 | − updatedAt: now, | |
| 344 | − }); | |
| 345 | − | |
| 346 | − if (targetRevision.movedFromRevisionId !== null) { | |
| 347 | − yield* cleanupOrphanedRevision({ | |
| 348 | − rows: input.rows, | |
| 349 | − recipeRevisionId: targetRevision.movedFromRevisionId, | |
| 350 | − }); | |
| 351 | − } | |
| 352 | − }); | |
| 353 | − | |
| 354 | −const createFreshTargetRevision = (input: { | |
| 355 | − currentRevision: StoredSourceRecipeRevisionRecord; | |
| 356 | − materializationHash: string; | |
| 357 | − now: number; | |
| 358 | − nextRevisionNumber: number; | |
| 359 | −}): { | |
| 360 | − revision: StoredSourceRecipeRevisionRecord; | |
| 361 | − movedFromRevisionId: StoredSourceRecipeRevisionRecord["id"] | null; | |
| 362 | −} => ({ | |
| 363 | − revision: { | |
| 364 | − ...input.currentRevision, | |
| 365 | − id: `src_recipe_rev_${crypto.randomUUID()}` as StoredSourceRecipeRevisionRecord["id"], | |
| 366 | − revisionNumber: input.nextRevisionNumber, | |
| 367 | − manifestJson: null, | |
| 368 | − manifestHash: null, | |
| 369 | − materializationHash: input.materializationHash, | |
| 370 | − createdAt: input.now, | |
| 371 | − updatedAt: input.now, | |
| 372 | − }, | |
| 373 | − movedFromRevisionId: input.currentRevision.id, | |
| 374 | −}); | |
| @@ -1,858 +0,0 @@ | ||
| 1 | −import { mkdtempSync } from "node:fs"; | |
| 2 | −import { tmpdir } from "node:os"; | |
| 3 | −import { join } from "node:path"; | |
| 4 | − | |
| 5 | −import { describe, expect, it } from "vitest"; | |
| 6 | −import * as Effect from "effect/Effect"; | |
| 7 | −import { | |
| 8 | − buildOpenApiToolPresentation, | |
| 9 | − compileOpenApiToolDefinitions, | |
| 10 | − extractOpenApiManifest, | |
| 11 | −} from "@executor/codemode-openapi"; | |
| 12 | − | |
| 13 | −import { | |
| 14 | − AccountIdSchema, | |
| 15 | − SourceIdSchema, | |
| 16 | − type LocalConfigSource, | |
| 17 | − SourceRecipeIdSchema, | |
| 18 | − SourceRecipeRevisionIdSchema, | |
| 19 | − WorkspaceIdSchema, | |
| 20 | − type Source, | |
| 21 | − type StoredSourceRecipeOperationRecord, | |
| 22 | −} from "#schema"; | |
| 23 | −import { | |
| 24 | − createSqlControlPlanePersistence, | |
| 25 | − type SqlControlPlanePersistence, | |
| 26 | −} from "#persistence"; | |
| 27 | − | |
| 28 | −import { | |
| 29 | − loadLocalExecutorConfig, | |
| 30 | − resolveLocalWorkspaceContext, | |
| 31 | − writeProjectLocalExecutorConfig, | |
| 32 | −} from "./local-config"; | |
| 33 | −import { | |
| 34 | − buildLocalSourceArtifact, | |
| 35 | − writeLocalSourceArtifact, | |
| 36 | −} from "./local-source-artifacts"; | |
| 37 | −import { | |
| 38 | − RuntimeLocalWorkspaceService, | |
| 39 | − type RuntimeLocalWorkspaceState, | |
| 40 | −} from "./local-runtime-context"; | |
| 41 | −import { | |
| 42 | − writeLocalWorkspaceState, | |
| 43 | −} from "./local-workspace-state"; | |
| 44 | −import { | |
| 45 | − expandRecipeTools, | |
| 46 | − loadSourceWithRecipe, | |
| 47 | − loadWorkspaceSourceRecipes, | |
| 48 | − recipeToolDescriptor, | |
| 49 | − recipeToolPath, | |
| 50 | − recipeToolSearchNamespace, | |
| 51 | − type LoadedSourceRecipe, | |
| 52 | −} from "./source-recipes-runtime"; | |
| 53 | − | |
| 54 | −const makePersistence = () => | |
| 55 | − Effect.runPromise( | |
| 56 | − createSqlControlPlanePersistence({ | |
| 57 | − localDataDir: ":memory:", | |
| 58 | − }), | |
| 59 | − ); | |
| 60 | − | |
| 61 | −const makeRuntimeLocalWorkspaceState = ( | |
| 62 | − workspaceId: ReturnType<typeof WorkspaceIdSchema.make>, | |
| 63 | −) => | |
| 64 | − Effect.runPromise( | |
| 65 | − Effect.gen(function* () { | |
| 66 | − const workspaceRoot = mkdtempSync( | |
| 67 | − join(tmpdir(), "executor-source-recipes-runtime-"), | |
| 68 | − ); | |
| 69 | − const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 70 | − const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 71 | − | |
| 72 | − return { | |
| 73 | − context, | |
| 74 | − installation: { | |
| 75 | − workspaceId, | |
| 76 | − accountId: AccountIdSchema.make("acc_local_source_recipes"), | |
| 77 | − }, | |
| 78 | − loadedConfig, | |
| 79 | − } satisfies RuntimeLocalWorkspaceState; | |
| 80 | − }), | |
| 81 | − ); | |
| 82 | − | |
| 83 | −const withRuntimeLocalWorkspace = <A, E>( | |
| 84 | − effect: Effect.Effect<A, E, never>, | |
| 85 | − runtimeLocalWorkspace: RuntimeLocalWorkspaceState, | |
| 86 | −) => | |
| 87 | − effect.pipe( | |
| 88 | − Effect.provideService(RuntimeLocalWorkspaceService, runtimeLocalWorkspace), | |
| 89 | − ); | |
| 90 | − | |
| 91 | −const openApiBindingConfigJson = (specUrl: string): string => | |
| 92 | − JSON.stringify({ | |
| 93 | − adapterKey: "openapi", | |
| 94 | − version: 1, | |
| 95 | − payload: { | |
| 96 | − specUrl, | |
| 97 | − defaultHeaders: null, | |
| 98 | − }, | |
| 99 | − }); | |
| 100 | − | |
| 101 | −const graphqlBindingConfigJson = (): string => | |
| 102 | − JSON.stringify({ | |
| 103 | − adapterKey: "graphql", | |
| 104 | − version: 1, | |
| 105 | − payload: { | |
| 106 | − defaultHeaders: null, | |
| 107 | − }, | |
| 108 | − }); | |
| 109 | − | |
| 110 | −const makeSource = (overrides: Partial<Source> = {}): Source => { | |
| 111 | − const kind = overrides.kind ?? "openapi"; | |
| 112 | − const endpoint = overrides.endpoint | |
| 113 | − ?? (kind === "graphql" | |
| 114 | − ? "https://example.com/graphql" | |
| 115 | − : kind === "mcp" | |
| 116 | − ? "https://example.com/mcp" | |
| 117 | − : "https://api.github.com"); | |
| 118 | − const binding = | |
| 119 | − overrides.binding | |
| 120 | − ?? (kind === "openapi" | |
| 121 | − ? { | |
| 122 | − specUrl: "https://api.github.com/openapi.json", | |
| 123 | − defaultHeaders: null, | |
| 124 | − } | |
| 125 | − : kind === "graphql" | |
| 126 | − ? { | |
| 127 | − defaultHeaders: null, | |
| 128 | − } | |
| 129 | − : kind === "mcp" | |
| 130 | − ? { | |
| 131 | − transport: null, | |
| 132 | − queryParams: null, | |
| 133 | − headers: null, | |
| 134 | − } | |
| 135 | − : {}); | |
| 136 | − | |
| 137 | − return { | |
| 138 | − id: SourceIdSchema.make("src_runtime_recipe"), | |
| 139 | − workspaceId: WorkspaceIdSchema.make("ws_runtime_recipe"), | |
| 140 | − name: "GitHub", | |
| 141 | − kind, | |
| 142 | − endpoint, | |
| 143 | − status: "connected", | |
| 144 | − enabled: true, | |
| 145 | − namespace: "github", | |
| 146 | − bindingVersion: 1, | |
| 147 | − binding, | |
| 148 | − importAuthPolicy: "reuse_runtime", | |
| 149 | − importAuth: { kind: "none" }, | |
| 150 | − auth: { kind: "none" }, | |
| 151 | − sourceHash: null, | |
| 152 | − lastError: null, | |
| 153 | − createdAt: 1000, | |
| 154 | − updatedAt: 1000, | |
| 155 | − ...overrides, | |
| 156 | − }; | |
| 157 | −}; | |
| 158 | − | |
| 159 | −const makeOperation = ( | |
| 160 | − overrides: Partial<StoredSourceRecipeOperationRecord> = {}, | |
| 161 | −): StoredSourceRecipeOperationRecord => ({ | |
| 162 | − id: "src_recipe_op_runtime", | |
| 163 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_runtime"), | |
| 164 | − operationKey: "getRepo", | |
| 165 | − transportKind: "http", | |
| 166 | − toolId: "getRepo", | |
| 167 | − title: "Get Repo", | |
| 168 | − description: "Read a repository", | |
| 169 | − operationKind: "read", | |
| 170 | − searchText: "get repo github", | |
| 171 | − inputSchemaJson: JSON.stringify({ | |
| 172 | − type: "object", | |
| 173 | − additionalProperties: false, | |
| 174 | − }), | |
| 175 | − outputSchemaJson: JSON.stringify({ | |
| 176 | − type: "object", | |
| 177 | − properties: { | |
| 178 | − full_name: { type: "string" }, | |
| 179 | − }, | |
| 180 | − }), | |
| 181 | − providerKind: "openapi", | |
| 182 | − providerDataJson: JSON.stringify({ | |
| 183 | − kind: "openapi", | |
| 184 | − toolId: "getRepo", | |
| 185 | − rawToolId: "repos_getRepo", | |
| 186 | − operationId: "repos.getRepo", | |
| 187 | − group: "repos", | |
| 188 | − leaf: "getRepo", | |
| 189 | − tags: ["repos"], | |
| 190 | − method: "get", | |
| 191 | − path: "/repos/{owner}/{repo}", | |
| 192 | − operationHash: "hash", | |
| 193 | − invocation: { | |
| 194 | − method: "get", | |
| 195 | − pathTemplate: "/repos/{owner}/{repo}", | |
| 196 | − parameters: [], | |
| 197 | − requestBody: null, | |
| 198 | − }, | |
| 199 | − }), | |
| 200 | − createdAt: 1000, | |
| 201 | − updatedAt: 1000, | |
| 202 | − ...overrides, | |
| 203 | −}); | |
| 204 | − | |
| 205 | −const makeGraphqlOperation = ( | |
| 206 | − overrides: Partial<StoredSourceRecipeOperationRecord> = {}, | |
| 207 | −): StoredSourceRecipeOperationRecord => ({ | |
| 208 | − id: "src_recipe_op_graphql_runtime", | |
| 209 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_runtime"), | |
| 210 | − operationKey: "viewer", | |
| 211 | − transportKind: "graphql", | |
| 212 | − toolId: "viewer", | |
| 213 | − title: "Viewer", | |
| 214 | − description: "Query the current viewer", | |
| 215 | − operationKind: "read", | |
| 216 | − searchText: "viewer graphql query", | |
| 217 | − inputSchemaJson: JSON.stringify({ | |
| 218 | − type: "object", | |
| 219 | − additionalProperties: false, | |
| 220 | − }), | |
| 221 | − outputSchemaJson: JSON.stringify({ | |
| 222 | − type: "object", | |
| 223 | − properties: { | |
| 224 | − login: { type: "string" }, | |
| 225 | − }, | |
| 226 | − }), | |
| 227 | − providerKind: "graphql", | |
| 228 | − providerDataJson: JSON.stringify({ | |
| 229 | − kind: "graphql", | |
| 230 | − toolKind: "field", | |
| 231 | − toolId: "viewer", | |
| 232 | − rawToolId: "viewer", | |
| 233 | − group: "query", | |
| 234 | − leaf: "viewer", | |
| 235 | − fieldName: "viewer", | |
| 236 | − operationType: "query", | |
| 237 | − operationName: "viewer", | |
| 238 | − operationDocument: "query Viewer { viewer { login } }", | |
| 239 | − queryTypeName: "Query", | |
| 240 | − mutationTypeName: null, | |
| 241 | − subscriptionTypeName: null, | |
| 242 | − }), | |
| 243 | − createdAt: 1000, | |
| 244 | − updatedAt: 1000, | |
| 245 | − ...overrides, | |
| 246 | −}); | |
| 247 | − | |
| 248 | −const makeMcpOperation = ( | |
| 249 | − overrides: Partial<StoredSourceRecipeOperationRecord> = {}, | |
| 250 | −): StoredSourceRecipeOperationRecord => ({ | |
| 251 | − id: "src_recipe_op_mcp_runtime", | |
| 252 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_runtime"), | |
| 253 | − operationKey: "echo", | |
| 254 | − transportKind: "mcp", | |
| 255 | − toolId: "echo", | |
| 256 | − title: "Echo", | |
| 257 | − description: "Echo a value", | |
| 258 | − operationKind: "unknown", | |
| 259 | − searchText: "echo mcp", | |
| 260 | − inputSchemaJson: null, | |
| 261 | − outputSchemaJson: null, | |
| 262 | − providerKind: "mcp", | |
| 263 | − providerDataJson: JSON.stringify({ | |
| 264 | − kind: "mcp", | |
| 265 | − toolId: "echo", | |
| 266 | − toolName: "echo", | |
| 267 | − description: "Echo a value", | |
| 268 | − }), | |
| 269 | − createdAt: 1000, | |
| 270 | − updatedAt: 1000, | |
| 271 | − ...overrides, | |
| 272 | −}); | |
| 273 | − | |
| 274 | −const configSourceFromSource = (source: Source): LocalConfigSource => { | |
| 275 | − switch (source.kind) { | |
| 276 | − case "openapi": | |
| 277 | − return { | |
| 278 | − kind: "openapi" as const, | |
| 279 | − ...(source.name !== source.id ? { name: source.name } : {}), | |
| 280 | − ...(source.namespace && source.namespace !== source.id | |
| 281 | − ? { namespace: source.namespace } | |
| 282 | − : {}), | |
| 283 | − ...(source.enabled ? {} : { enabled: false }), | |
| 284 | − connection: { | |
| 285 | − endpoint: source.endpoint, | |
| 286 | − }, | |
| 287 | − binding: { | |
| 288 | − specUrl: | |
| 289 | − typeof source.binding.specUrl === "string" | |
| 290 | − ? source.binding.specUrl | |
| 291 | − : "https://example.com/openapi.json", | |
| 292 | − defaultHeaders: | |
| 293 | − (source.binding.defaultHeaders as Record<string, string> | null | undefined) | |
| 294 | − ?? null, | |
| 295 | − }, | |
| 296 | − }; | |
| 297 | − case "graphql": | |
| 298 | − return { | |
| 299 | − kind: "graphql" as const, | |
| 300 | − ...(source.name !== source.id ? { name: source.name } : {}), | |
| 301 | − ...(source.namespace && source.namespace !== source.id | |
| 302 | − ? { namespace: source.namespace } | |
| 303 | − : {}), | |
| 304 | − ...(source.enabled ? {} : { enabled: false }), | |
| 305 | − connection: { | |
| 306 | − endpoint: source.endpoint, | |
| 307 | − }, | |
| 308 | − binding: { | |
| 309 | − defaultHeaders: | |
| 310 | − (source.binding.defaultHeaders as Record<string, string> | null | undefined) | |
| 311 | − ?? null, | |
| 312 | − }, | |
| 313 | − }; | |
| 314 | − case "mcp": | |
| 315 | − return { | |
| 316 | − kind: "mcp" as const, | |
| 317 | − ...(source.name !== source.id ? { name: source.name } : {}), | |
| 318 | − ...(source.namespace && source.namespace !== source.id | |
| 319 | − ? { namespace: source.namespace } | |
| 320 | − : {}), | |
| 321 | − ...(source.enabled ? {} : { enabled: false }), | |
| 322 | − connection: { | |
| 323 | − endpoint: source.endpoint, | |
| 324 | − }, | |
| 325 | − binding: { | |
| 326 | − transport: | |
| 327 | − (source.binding.transport as "auto" | "streamable-http" | "sse" | null | undefined) | |
| 328 | − ?? null, | |
| 329 | − queryParams: | |
| 330 | − (source.binding.queryParams as Record<string, string> | null | undefined) | |
| 331 | − ?? null, | |
| 332 | − headers: | |
| 333 | − (source.binding.headers as Record<string, string> | null | undefined) | |
| 334 | − ?? null, | |
| 335 | − }, | |
| 336 | − }; | |
| 337 | − default: | |
| 338 | − throw new Error(`Unsupported source kind in test fixture: ${source.kind}`); | |
| 339 | − } | |
| 340 | −}; | |
| 341 | − | |
| 342 | −const seedLocalWorkspaceSources = async (input: { | |
| 343 | − runtimeLocalWorkspace: RuntimeLocalWorkspaceState; | |
| 344 | − fixtures: ReadonlyArray<{ | |
| 345 | − source: Source; | |
| 346 | − materialization: { | |
| 347 | − manifestJson: string | null; | |
| 348 | − manifestHash: string | null; | |
| 349 | − sourceHash: string | null; | |
| 350 | − documents: ReadonlyArray<any>; | |
| 351 | − schemaBundles: ReadonlyArray<any>; | |
| 352 | − operations: ReadonlyArray<StoredSourceRecipeOperationRecord>; | |
| 353 | − }; | |
| 354 | − }>; | |
| 355 | −}) => { | |
| 356 | − await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 357 | − context: input.runtimeLocalWorkspace.context, | |
| 358 | − config: { | |
| 359 | − sources: Object.fromEntries( | |
| 360 | − input.fixtures.map(({ source }) => [source.id, configSourceFromSource(source)]), | |
| 361 | − ), | |
| 362 | − }, | |
| 363 | − })); | |
| 364 | − | |
| 365 | − await Promise.all( | |
| 366 | − input.fixtures.map(({ source, materialization }) => | |
| 367 | − Effect.runPromise(writeLocalSourceArtifact({ | |
| 368 | − context: input.runtimeLocalWorkspace.context, | |
| 369 | − sourceId: source.id, | |
| 370 | − artifact: buildLocalSourceArtifact({ | |
| 371 | − source, | |
| 372 | − materialization, | |
| 373 | − }), | |
| 374 | − })), | |
| 375 | − ), | |
| 376 | − ); | |
| 377 | − | |
| 378 | − await Effect.runPromise(writeLocalWorkspaceState({ | |
| 379 | − context: input.runtimeLocalWorkspace.context, | |
| 380 | − state: { | |
| 381 | − version: 1, | |
| 382 | − sources: Object.fromEntries( | |
| 383 | − input.fixtures.map(({ source }) => [ | |
| 384 | − source.id, | |
| 385 | − { | |
| 386 | − status: source.status, | |
| 387 | − lastError: source.lastError, | |
| 388 | − sourceHash: source.sourceHash, | |
| 389 | − createdAt: source.createdAt, | |
| 390 | − updatedAt: source.updatedAt, | |
| 391 | − }, | |
| 392 | − ]), | |
| 393 | − ), | |
| 394 | − policies: {}, | |
| 395 | − }, | |
| 396 | − })); | |
| 397 | −}; | |
| 398 | − | |
| 399 | −describe("source-recipes-runtime", () => { | |
| 400 | − describe("recipe tool helpers", () => { | |
| 401 | − it("derives tool paths from explicit and implicit namespaces", () => { | |
| 402 | − const source = makeSource(); | |
| 403 | − const operation = makeOperation(); | |
| 404 | − | |
| 405 | − expect(recipeToolPath({ | |
| 406 | − source, | |
| 407 | − operation, | |
| 408 | − })).toBe("github.getRepo"); | |
| 409 | − expect(recipeToolPath({ | |
| 410 | − source: makeSource({ | |
| 411 | − namespace: null, | |
| 412 | − name: "My GitHub", | |
| 413 | − }), | |
| 414 | − operation, | |
| 415 | − })).toBe("my.github.getRepo"); | |
| 416 | − expect(recipeToolPath({ | |
| 417 | − source: makeSource({ | |
| 418 | − namespace: "", | |
| 419 | − }), | |
| 420 | − operation, | |
| 421 | − })).toBe("getRepo"); | |
| 422 | − }); | |
| 423 | − | |
| 424 | − it("derives search namespaces from path segments and graphql namespaces", () => { | |
| 425 | − const openApiSource = makeSource(); | |
| 426 | − const graphqlSource = makeSource({ | |
| 427 | − kind: "graphql", | |
| 428 | − namespace: "issues", | |
| 429 | − }); | |
| 430 | − | |
| 431 | − expect(recipeToolSearchNamespace({ | |
| 432 | − source: openApiSource, | |
| 433 | − path: "github.getRepo", | |
| 434 | − operation: makeOperation(), | |
| 435 | − })).toBe("github.getRepo"); | |
| 436 | − expect(recipeToolSearchNamespace({ | |
| 437 | − source: openApiSource, | |
| 438 | − path: "a.b.c", | |
| 439 | − operation: makeOperation(), | |
| 440 | − })).toBe("a.b"); | |
| 441 | − expect(recipeToolSearchNamespace({ | |
| 442 | − source: graphqlSource, | |
| 443 | − path: "ignored.path", | |
| 444 | − operation: makeGraphqlOperation(), | |
| 445 | − })).toBe("issues"); | |
| 446 | − }); | |
| 447 | − | |
| 448 | − it("computes interaction modes and descriptor fields correctly", () => { | |
| 449 | − expect(recipeToolDescriptor({ | |
| 450 | − source: makeSource(), | |
| 451 | − operation: makeOperation(), | |
| 452 | − path: "github.getRepo", | |
| 453 | − includeSchemas: true, | |
| 454 | − }).interaction).toBe("auto"); | |
| 455 | − | |
| 456 | − expect(recipeToolDescriptor({ | |
| 457 | − source: makeSource(), | |
| 458 | − operation: makeOperation({ | |
| 459 | − operationKind: "delete", | |
| 460 | − }), | |
| 461 | − path: "github.deleteRepo", | |
| 462 | − includeSchemas: true, | |
| 463 | − }).interaction).toBe("required"); | |
| 464 | − | |
| 465 | − expect(recipeToolDescriptor({ | |
| 466 | − source: makeSource({ | |
| 467 | − kind: "graphql", | |
| 468 | − }), | |
| 469 | − operation: makeGraphqlOperation(), | |
| 470 | − path: "graphql.viewer", | |
| 471 | − includeSchemas: true, | |
| 472 | − }).interaction).toBe("auto"); | |
| 473 | − | |
| 474 | − expect(recipeToolDescriptor({ | |
| 475 | − source: makeSource({ | |
| 476 | − kind: "graphql", | |
| 477 | − }), | |
| 478 | − operation: makeGraphqlOperation({ | |
| 479 | − operationKind: "write", | |
| 480 | − }), | |
| 481 | − path: "graphql.createIssue", | |
| 482 | − includeSchemas: true, | |
| 483 | − }).interaction).toBe("required"); | |
| 484 | − | |
| 485 | − expect(recipeToolDescriptor({ | |
| 486 | − source: makeSource({ | |
| 487 | − kind: "mcp", | |
| 488 | − binding: { | |
| 489 | − transport: "streamable-http", | |
| 490 | − queryParams: null, | |
| 491 | − headers: null, | |
| 492 | − }, | |
| 493 | − }), | |
| 494 | − operation: makeMcpOperation(), | |
| 495 | − path: "mcp.echo", | |
| 496 | − includeSchemas: true, | |
| 497 | − }).interaction).toBe("auto"); | |
| 498 | − | |
| 499 | − const descriptor = recipeToolDescriptor({ | |
| 500 | − source: makeSource(), | |
| 501 | − operation: makeOperation({ | |
| 502 | − description: null, | |
| 503 | − title: "Fallback Title", | |
| 504 | − providerDataJson: null, | |
| 505 | − }), | |
| 506 | − path: "github.getRepo", | |
| 507 | − includeSchemas: false, | |
| 508 | − }); | |
| 509 | − | |
| 510 | − expect(descriptor.description).toBe("Fallback Title"); | |
| 511 | − expect(descriptor.inputSchemaJson).toBeUndefined(); | |
| 512 | − expect(descriptor.outputSchemaJson).toBeUndefined(); | |
| 513 | − expect(descriptor).not.toHaveProperty("providerDataJson"); | |
| 514 | − }); | |
| 515 | − | |
| 516 | − it("expands recipes into lower-cased searchable tools and handles empty operation sets", () => { | |
| 517 | − const recipe: LoadedSourceRecipe = { | |
| 518 | − source: makeSource({ | |
| 519 | − name: "GITHUB API", | |
| 520 | − }), | |
| 521 | − sourceRecord: { | |
| 522 | − id: SourceIdSchema.make("src_runtime_recipe"), | |
| 523 | − workspaceId: WorkspaceIdSchema.make("ws_runtime_recipe"), | |
| 524 | − recipeId: SourceRecipeIdSchema.make("src_recipe_runtime"), | |
| 525 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_runtime"), | |
| 526 | − name: "GITHUB API", | |
| 527 | − kind: "openapi", | |
| 528 | − endpoint: "https://api.github.com", | |
| 529 | − status: "connected", | |
| 530 | − enabled: true, | |
| 531 | − namespace: "github", | |
| 532 | − importAuthPolicy: "reuse_runtime", | |
| 533 | − bindingConfigJson: openApiBindingConfigJson("https://api.github.com/openapi.json"), | |
| 534 | − sourceHash: null, | |
| 535 | − lastError: null, | |
| 536 | − createdAt: 1000, | |
| 537 | − updatedAt: 1000, | |
| 538 | − }, | |
| 539 | − revision: { | |
| 540 | − id: SourceRecipeRevisionIdSchema.make("src_recipe_rev_runtime"), | |
| 541 | − recipeId: SourceRecipeIdSchema.make("src_recipe_runtime"), | |
| 542 | − revisionNumber: 1, | |
| 543 | − sourceConfigJson: "{}", | |
| 544 | − manifestJson: null, | |
| 545 | − manifestHash: null, | |
| 546 | − materializationHash: null, | |
| 547 | − createdAt: 1000, | |
| 548 | − updatedAt: 1000, | |
| 549 | − }, | |
| 550 | − documents: [], | |
| 551 | − schemaBundles: [], | |
| 552 | − operations: [makeOperation({ | |
| 553 | − searchText: "", | |
| 554 | − })], | |
| 555 | − manifest: null, | |
| 556 | − }; | |
| 557 | − | |
| 558 | − const expanded = Effect.runSync( | |
| 559 | − expandRecipeTools({ | |
| 560 | − recipes: [recipe], | |
| 561 | − includeSchemas: false, | |
| 562 | − }), | |
| 563 | − ); | |
| 564 | − expect(expanded).toHaveLength(1); | |
| 565 | − expect(expanded[0]?.searchText).toBe( | |
| 566 | − "github.getrepo github.getrepo github api github.getrepo getrepo get repo read a repository repos_getrepo repos.getrepo get /repos/{owner}/{repo} repos getrepo repos", | |
| 567 | − ); | |
| 568 | − | |
| 569 | − expect(Effect.runSync(expandRecipeTools({ | |
| 570 | − recipes: [{ | |
| 571 | − ...recipe, | |
| 572 | − operations: [], | |
| 573 | − }], | |
| 574 | − includeSchemas: false, | |
| 575 | − }))).toEqual([]); | |
| 576 | − }); | |
| 577 | − }); | |
| 578 | − | |
| 579 | − describe("recipe loading", () => { | |
| 580 | − it("loads multiple sources sharing the same recipe revision", async () => { | |
| 581 | − const persistence = await makePersistence(); | |
| 582 | − try { | |
| 583 | − const workspaceId = WorkspaceIdSchema.make("ws_shared_revision"); | |
| 584 | − const runtimeLocalWorkspace = | |
| 585 | − await makeRuntimeLocalWorkspaceState(workspaceId); | |
| 586 | − const openApiDocument = JSON.stringify({ | |
| 587 | − openapi: "3.0.3", | |
| 588 | − info: { | |
| 589 | − title: "GitHub", | |
| 590 | − version: "1.0.0", | |
| 591 | − }, | |
| 592 | − paths: { | |
| 593 | − "/repos/{owner}/{repo}": { | |
| 594 | − get: { | |
| 595 | − operationId: "repos.getRepo", | |
| 596 | − parameters: [ | |
| 597 | − { | |
| 598 | − name: "owner", | |
| 599 | − in: "path", | |
| 600 | − required: true, | |
| 601 | − schema: { type: "string" }, | |
| 602 | − }, | |
| 603 | − { | |
| 604 | − name: "repo", | |
| 605 | − in: "path", | |
| 606 | − required: true, | |
| 607 | − schema: { type: "string" }, | |
| 608 | − }, | |
| 609 | − ], | |
| 610 | − responses: { | |
| 611 | − 200: { | |
| 612 | − description: "ok", | |
| 613 | − }, | |
| 614 | − }, | |
| 615 | − }, | |
| 616 | − }, | |
| 617 | − }, | |
| 618 | − }); | |
| 619 | − const manifest = await Effect.runPromise(extractOpenApiManifest("GitHub", openApiDocument)); | |
| 620 | − const definition = compileOpenApiToolDefinitions(manifest)[0]!; | |
| 621 | − const presentation = buildOpenApiToolPresentation({ | |
| 622 | − definition, | |
| 623 | − }); | |
| 624 | − | |
| 625 | − await seedLocalWorkspaceSources({ | |
| 626 | − runtimeLocalWorkspace, | |
| 627 | − fixtures: ["GitHub One", "GitHub Two"].map((name, index) => ({ | |
| 628 | − source: makeSource({ | |
| 629 | − id: SourceIdSchema.make(`src_shared_revision_${index}`), | |
| 630 | − workspaceId, | |
| 631 | − name, | |
| 632 | − sourceHash: manifest.sourceHash, | |
| 633 | − createdAt: 1000 + index, | |
| 634 | − updatedAt: 1000 + index, | |
| 635 | − }), | |
| 636 | − materialization: { | |
| 637 | − manifestJson: JSON.stringify(manifest), | |
| 638 | − manifestHash: manifest.sourceHash, | |
| 639 | − sourceHash: manifest.sourceHash, | |
| 640 | − documents: [{ | |
| 641 | − id: `src_recipe_doc_shared_revision_${index}`, | |
| 642 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_materialization"), | |
| 643 | − documentKind: "openapi", | |
| 644 | − documentKey: "https://api.github.com/openapi.json", | |
| 645 | − contentText: openApiDocument, | |
| 646 | − contentHash: manifest.sourceHash, | |
| 647 | − fetchedAt: 1000, | |
| 648 | − createdAt: 1000, | |
| 649 | − updatedAt: 1000, | |
| 650 | − }], | |
| 651 | − schemaBundles: [], | |
| 652 | − operations: [makeOperation({ | |
| 653 | − id: `src_recipe_op_shared_revision_${index}`, | |
| 654 | − recipeRevisionId: SourceRecipeRevisionIdSchema.make("src_recipe_rev_materialization"), | |
| 655 | − operationKey: definition.toolId, | |
| 656 | − toolId: definition.toolId, | |
| 657 | − title: definition.name, | |
| 658 | − description: definition.description, | |
| 659 | − searchText: `${definition.toolId} ${definition.name}`.toLowerCase(), | |
| 660 | − inputSchemaJson: presentation.inputSchemaJson ?? null, | |
| 661 | − outputSchemaJson: presentation.outputSchemaJson ?? null, | |
| 662 | − providerDataJson: presentation.providerDataJson, | |
| 663 | − })], | |
| 664 | − }, | |
| 665 | − })), | |
| 666 | − }); | |
| 667 | − | |
| 668 | − const recipes = await Effect.runPromise( | |
| 669 | − withRuntimeLocalWorkspace( | |
| 670 | − loadWorkspaceSourceRecipes({ | |
| 671 | − rows: persistence.rows, | |
| 672 | − workspaceId, | |
| 673 | − }), | |
| 674 | − runtimeLocalWorkspace, | |
| 675 | − ), | |
| 676 | − ); | |
| 677 | − | |
| 678 | − expect(recipes).toHaveLength(2); | |
| 679 | − expect(recipes[0]?.documents).toHaveLength(1); | |
| 680 | − expect(recipes[0]?.operations).toHaveLength(1); | |
| 681 | − expect(recipes[0]?.documents[0]?.contentText).toBe( | |
| 682 | − recipes[1]?.documents[0]?.contentText, | |
| 683 | − ); | |
| 684 | − expect(recipes[0]?.operations[0]?.providerDataJson).toBe( | |
| 685 | − recipes[1]?.operations[0]?.providerDataJson, | |
| 686 | − ); | |
| 687 | − } finally { | |
| 688 | − await persistence.close(); | |
| 689 | − } | |
| 690 | − }, 60_000); | |
| 691 | − | |
| 692 | − it("loads sources with empty recipe documents and operations", async () => { | |
| 693 | − const persistence = await makePersistence(); | |
| 694 | − try { | |
| 695 | − const workspaceId = WorkspaceIdSchema.make("ws_empty_recipe_rows"); | |
| 696 | − const sourceId = SourceIdSchema.make("src_empty_recipe_rows"); | |
| 697 | − const runtimeLocalWorkspace = | |
| 698 | − await makeRuntimeLocalWorkspaceState(workspaceId); | |
| 699 | − | |
| 700 | − await seedLocalWorkspaceSources({ | |
| 701 | − runtimeLocalWorkspace, | |
| 702 | − fixtures: [{ | |
| 703 | − source: makeSource({ | |
| 704 | − id: sourceId, | |
| 705 | − workspaceId, | |
| 706 | − name: "GraphQL Demo", | |
| 707 | − kind: "graphql", | |
| 708 | − endpoint: "https://example.com/graphql", | |
| 709 | − namespace: "graphql", | |
| 710 | − binding: { | |
| 711 | − defaultHeaders: null, | |
| 712 | − }, | |
| 713 | − }), | |
| 714 | − materialization: { | |
| 715 | − manifestJson: null, | |
| 716 | − manifestHash: null, | |
| 717 | − sourceHash: null, | |
| 718 | − documents: [], | |
| 719 | − schemaBundles: [], | |
| 720 | − operations: [], | |
| 721 | − }, | |
| 722 | − }], | |
| 723 | − }); | |
| 724 | − | |
| 725 | − const recipes = await Effect.runPromise( | |
| 726 | − withRuntimeLocalWorkspace( | |
| 727 | − loadWorkspaceSourceRecipes({ | |
| 728 | − rows: persistence.rows, | |
| 729 | − workspaceId, | |
| 730 | − }), | |
| 731 | − runtimeLocalWorkspace, | |
| 732 | − ), | |
| 733 | − ); | |
| 734 | − | |
| 735 | − expect(recipes).toHaveLength(1); | |
| 736 | − expect(recipes[0]?.documents).toEqual([]); | |
| 737 | − expect(recipes[0]?.operations).toEqual([]); | |
| 738 | − expect(recipes[0]?.manifest).toBeNull(); | |
| 739 | − } finally { | |
| 740 | − await persistence.close(); | |
| 741 | − } | |
| 742 | − }, 60_000); | |
| 743 | − | |
| 744 | − it("fails clearly when loading a missing source, missing revision, or invalid manifest", async () => { | |
| 745 | − const persistence = await makePersistence(); | |
| 746 | − try { | |
| 747 | − const missingWorkspaceId = WorkspaceIdSchema.make("ws_missing_source"); | |
| 748 | − const missingRuntimeLocalWorkspace = | |
| 749 | − await makeRuntimeLocalWorkspaceState(missingWorkspaceId); | |
| 750 | − | |
| 751 | − await expect( | |
| 752 | − Effect.runPromise( | |
| 753 | − withRuntimeLocalWorkspace( | |
| 754 | − loadSourceWithRecipe({ | |
| 755 | − rows: persistence.rows, | |
| 756 | − workspaceId: missingWorkspaceId, | |
| 757 | − sourceId: SourceIdSchema.make("src_missing_source"), | |
| 758 | − }), | |
| 759 | − missingRuntimeLocalWorkspace, | |
| 760 | − ), | |
| 761 | − ), | |
| 762 | − ).rejects.toThrow("Source not found"); | |
| 763 | − | |
| 764 | − const workspaceId = WorkspaceIdSchema.make("ws_bad_recipe_runtime"); | |
| 765 | − const sourceId = SourceIdSchema.make("src_bad_recipe_runtime"); | |
| 766 | − const runtimeLocalWorkspace = | |
| 767 | − await makeRuntimeLocalWorkspaceState(workspaceId); | |
| 768 | − | |
| 769 | − await Effect.runPromise(writeProjectLocalExecutorConfig({ | |
| 770 | − context: runtimeLocalWorkspace.context, | |
| 771 | − config: { | |
| 772 | − sources: { | |
| 773 | − [sourceId]: configSourceFromSource( | |
| 774 | − makeSource({ | |
| 775 | − id: sourceId, | |
| 776 | − workspaceId, | |
| 777 | − name: "Broken GitHub", | |
| 778 | − }), | |
| 779 | − ), | |
| 780 | − }, | |
| 781 | − }, | |
| 782 | − })); | |
| 783 | − await Effect.runPromise(writeLocalWorkspaceState({ | |
| 784 | − context: runtimeLocalWorkspace.context, | |
| 785 | − state: { | |
| 786 | − version: 1, | |
| 787 | − sources: { | |
| 788 | − [sourceId]: { | |
| 789 | − status: "connected", | |
| 790 | − lastError: null, | |
| 791 | − sourceHash: null, | |
| 792 | − createdAt: 1000, | |
| 793 | − updatedAt: 1000, | |
| 794 | − }, | |
| 795 | − }, | |
| 796 | − policies: {}, | |
| 797 | − }, | |
| 798 | − })); | |
| 799 | − | |
| 800 | − await expect( | |
| 801 | − Effect.runPromise( | |
| 802 | − withRuntimeLocalWorkspace( | |
| 803 | − loadSourceWithRecipe({ | |
| 804 | − rows: persistence.rows, | |
| 805 | − workspaceId, | |
| 806 | − sourceId, | |
| 807 | − }), | |
| 808 | − runtimeLocalWorkspace, | |
| 809 | − ), | |
| 810 | − ), | |
| 811 | − ).rejects.toThrow("Recipe artifact missing"); | |
| 812 | − | |
| 813 | − const invalidManifestSource = makeSource({ | |
| 814 | − id: sourceId, | |
| 815 | − workspaceId, | |
| 816 | − name: "Broken GitHub", | |
| 817 | − }); | |
| 818 | − const invalidArtifact = buildLocalSourceArtifact({ | |
| 819 | − source: invalidManifestSource, | |
| 820 | − materialization: { | |
| 821 | − manifestJson: "{}", | |
| 822 | − manifestHash: "manifest_hash_invalid", | |
| 823 | − sourceHash: "manifest_hash_invalid", | |
| 824 | − documents: [], | |
| 825 | − schemaBundles: [], | |
| 826 | − operations: [], | |
| 827 | − }, | |
| 828 | − }); | |
| 829 | − await Effect.runPromise(writeLocalSourceArtifact({ | |
| 830 | − context: runtimeLocalWorkspace.context, | |
| 831 | − sourceId, | |
| 832 | − artifact: { | |
| 833 | − ...invalidArtifact, | |
| 834 | − revision: { | |
| 835 | − ...invalidArtifact.revision, | |
| 836 | − manifestJson: "{bad-json", | |
| 837 | − }, | |
| 838 | − }, | |
| 839 | − })); | |
| 840 | − | |
| 841 | − await expect( | |
| 842 | − Effect.runPromise( | |
| 843 | − withRuntimeLocalWorkspace( | |
| 844 | − loadSourceWithRecipe({ | |
| 845 | − rows: persistence.rows, | |
| 846 | − workspaceId, | |
| 847 | − sourceId, | |
| 848 | − }), | |
| 849 | − runtimeLocalWorkspace, | |
| 850 | − ), | |
| 851 | − ), | |
| 852 | − ).rejects.toThrow(`Invalid OpenAPI manifest for ${sourceId}`); | |
| 853 | − } finally { | |
| 854 | − await persistence.close(); | |
| 855 | − } | |
| 856 | − }); | |
| 857 | − }); | |
| 858 | −}); | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { |
| 2 | + type ToolCatalogEntry, | |
| 2 | 3 | type ToolDescriptor, |
| 3 | 4 | } from "@executor/codemode-core"; |
| 4 | −import type { SqlControlPlaneRows } from "#persistence"; | |
| 5 | 5 | import type { |
| 6 | 6 | AccountId, |
| 7 | 7 | Source, |
| @@ -13,19 +13,20 @@ import type { · 5 unmodified lines | ||
| 13 | 13 | WorkspaceId, |
| 14 | 14 | } from "#schema"; |
| 15 | 15 | import * as Effect from "effect/Effect"; |
| 16 | − | |
| 17 | −import { | |
| 18 | − readLocalSourceArtifact, | |
| 19 | −} from "./local-source-artifacts"; | |
| 20 | 16 | import { LocalSourceArtifactMissingError } from "./local-errors"; |
| 21 | 17 | import { |
| 22 | 18 | requireRuntimeLocalWorkspace, |
| 23 | 19 | } from "./local-runtime-context"; |
| 20 | +import { | |
| 21 | + type WorkspaceStorageServices, | |
| 22 | + SourceArtifactStore, | |
| 23 | +} from "./local-storage"; | |
| 24 | 24 | import { namespaceFromSourceName } from "./source-names"; |
| 25 | 25 | import { |
| 26 | 26 | getSourceAdapterForOperation, |
| 27 | 27 | getSourceAdapterForSource, |
| 28 | 28 | } from "./source-adapters"; |
| 29 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 29 | 30 | import type { SourceAdapterPersistedOperationMetadata } from "./source-adapters/types"; |
| 30 | 31 | import { firstSchemaBundle } from "./source-adapters/shared"; |
| 31 | 32 | import { loadSourceById, loadSourcesInWorkspace } from "./source-store"; |
| @@ -68,6 +69,16 @@ export type LoadedSourceRecipeToolIndexEntry = { · 36 unmodified lines | ||
| 68 | 69 | descriptor: ToolDescriptor; |
| 69 | 70 | }; |
| 70 | 71 | |
| 72 | +export const recipeToolCatalogEntry = (input: { | |
| 73 | + tool: LoadedSourceRecipeToolIndexEntry; | |
| 74 | + score: (queryTokens: readonly string[]) => number; | |
| 75 | +}): ToolCatalogEntry => ({ | |
| 76 | + descriptor: input.tool.descriptor, | |
| 77 | + namespace: input.tool.searchNamespace, | |
| 78 | + searchText: input.tool.searchText, | |
| 79 | + score: input.score, | |
| 80 | +}); | |
| 81 | + | |
| 71 | 82 | const parseManifestForRecipe = (input: { |
| 72 | 83 | source: Source; |
| 73 | 84 | revision: StoredSourceRecipeRevisionRecord; |
| @@ -157,18 +168,19 @@ const primarySchemaBundleForRevision = (input: { · 83 unmodified lines | ||
| 157 | 168 | }; |
| 158 | 169 | |
| 159 | 170 | export const loadWorkspaceSourceRecipes = (input: { |
| 160 | − rows: SqlControlPlaneRows; | |
| 171 | + rows: ControlPlaneStoreShape; | |
| 161 | 172 | workspaceId: WorkspaceId; |
| 162 | 173 | actorAccountId?: AccountId | null; |
| 163 | −}): Effect.Effect<readonly LoadedSourceRecipe[], Error, never> => | |
| 174 | +}): Effect.Effect<readonly LoadedSourceRecipe[], Error, WorkspaceStorageServices> => | |
| 164 | 175 | Effect.gen(function* () { |
| 165 | 176 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(input.workspaceId); |
| 177 | + const sourceArtifactStore = yield* SourceArtifactStore; | |
| 166 | 178 | const sources = yield* loadSourcesInWorkspace(input.rows, input.workspaceId, { |
| 167 | 179 | actorAccountId: input.actorAccountId, |
| 168 | 180 | }); |
| 169 | 181 | const localRecipes = yield* Effect.forEach(sources, (source) => |
| 170 | 182 | Effect.gen(function* () { |
| 171 | − const artifact = yield* readLocalSourceArtifact({ | |
| 183 | + const artifact = yield* sourceArtifactStore.read({ | |
| 172 | 184 | context: runtimeLocalWorkspace.context, |
| 173 | 185 | sourceId: source.id, |
| 174 | 186 | }); |
| @@ -215,19 +227,20 @@ export const loadWorkspaceSourceRecipes = (input: { · 40 unmodified lines | ||
| 215 | 227 | }); |
| 216 | 228 | |
| 217 | 229 | export const loadSourceWithRecipe = (input: { |
| 218 | − rows: SqlControlPlaneRows; | |
| 230 | + rows: ControlPlaneStoreShape; | |
| 219 | 231 | workspaceId: WorkspaceId; |
| 220 | 232 | sourceId: Source["id"]; |
| 221 | 233 | actorAccountId?: AccountId | null; |
| 222 | −}): Effect.Effect<LoadedSourceRecipe, Error | LocalSourceArtifactMissingError, never> => | |
| 234 | +}): Effect.Effect<LoadedSourceRecipe, Error | LocalSourceArtifactMissingError, WorkspaceStorageServices> => | |
| 223 | 235 | Effect.gen(function* () { |
| 224 | 236 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(input.workspaceId); |
| 237 | + const sourceArtifactStore = yield* SourceArtifactStore; | |
| 225 | 238 | const source = yield* loadSourceById(input.rows, { |
| 226 | 239 | workspaceId: input.workspaceId, |
| 227 | 240 | sourceId: input.sourceId, |
| 228 | 241 | actorAccountId: input.actorAccountId, |
| 229 | 242 | }); |
| 230 | − const artifact = yield* readLocalSourceArtifact({ | |
| 243 | + const artifact = yield* sourceArtifactStore.read({ | |
| 231 | 244 | context: runtimeLocalWorkspace.context, |
| 232 | 245 | sourceId: source.id, |
| 233 | 246 | }); |
| @@ -335,11 +348,11 @@ export const expandRecipeTools = (input: { · 101 unmodified lines | ||
| 335 | 348 | ); |
| 336 | 349 | |
| 337 | 350 | export const loadWorkspaceSourceRecipeToolIndex = (input: { |
| 338 | − rows: SqlControlPlaneRows; | |
| 351 | + rows: ControlPlaneStoreShape; | |
| 339 | 352 | workspaceId: WorkspaceId; |
| 340 | 353 | actorAccountId?: AccountId | null; |
| 341 | 354 | includeSchemas: boolean; |
| 342 | −}): Effect.Effect<readonly LoadedSourceRecipeToolIndexEntry[], Error, never> => | |
| 355 | +}): Effect.Effect<readonly LoadedSourceRecipeToolIndexEntry[], Error, WorkspaceStorageServices> => | |
| 343 | 356 | Effect.gen(function* () { |
| 344 | 357 | const recipes = yield* loadWorkspaceSourceRecipes({ |
| 345 | 358 | rows: input.rows, |
| @@ -364,12 +377,12 @@ export const loadWorkspaceSourceRecipeToolIndex = (input: { · 18 unmodified lines | ||
| 364 | 377 | }); |
| 365 | 378 | |
| 366 | 379 | export const loadWorkspaceSourceRecipeToolByPath = (input: { |
| 367 | − rows: SqlControlPlaneRows; | |
| 380 | + rows: ControlPlaneStoreShape; | |
| 368 | 381 | workspaceId: WorkspaceId; |
| 369 | 382 | path: string; |
| 370 | 383 | actorAccountId?: AccountId | null; |
| 371 | 384 | includeSchemas: boolean; |
| 372 | −}): Effect.Effect<LoadedSourceRecipeToolIndexEntry | null, Error, never> => | |
| 385 | +}): Effect.Effect<LoadedSourceRecipeToolIndexEntry | null, Error, WorkspaceStorageServices> => | |
| 373 | 386 | Effect.gen(function* () { |
| 374 | 387 | const recipes = yield* loadWorkspaceSourceRecipes({ |
| 375 | 388 | rows: input.rows, |
| @@ -1,310 +0,0 @@ | ||
| 1 | −import { mkdtempSync } from "node:fs"; | |
| 2 | −import { tmpdir } from "node:os"; | |
| 3 | −import { join } from "node:path"; | |
| 4 | − | |
| 5 | −import { describe, expect, it } from "@effect/vitest"; | |
| 6 | − | |
| 7 | −import { | |
| 8 | − AccountIdSchema, | |
| 9 | − decodeBuiltInAuthArtifactConfig, | |
| 10 | − McpSourceAuthSessionDataJsonSchema, | |
| 11 | − SecretMaterialIdSchema, | |
| 12 | − SourceAuthSessionIdSchema, | |
| 13 | − SourceIdSchema, | |
| 14 | − WorkspaceIdSchema, | |
| 15 | − type Source, | |
| 16 | −} from "#schema"; | |
| 17 | −import * as Effect from "effect/Effect"; | |
| 18 | −import * as Option from "effect/Option"; | |
| 19 | −import * as Schema from "effect/Schema"; | |
| 20 | −import * as Scope from "effect/Scope"; | |
| 21 | − | |
| 22 | −import { | |
| 23 | − createSqlControlPlanePersistence, | |
| 24 | − type SqlControlPlanePersistence, | |
| 25 | −} from "../persistence"; | |
| 26 | −import { | |
| 27 | − loadLocalExecutorConfig, | |
| 28 | − resolveLocalWorkspaceContext, | |
| 29 | −} from "./local-config"; | |
| 30 | −import { | |
| 31 | − RuntimeLocalWorkspaceService, | |
| 32 | − type RuntimeLocalWorkspaceState, | |
| 33 | −} from "./local-runtime-context"; | |
| 34 | −import { | |
| 35 | − loadLocalWorkspaceState, | |
| 36 | −} from "./local-workspace-state"; | |
| 37 | −import { persistSource, removeSourceById } from "./source-store"; | |
| 38 | − | |
| 39 | −const makePersistence: Effect.Effect<SqlControlPlanePersistence, unknown, Scope.Scope> = | |
| 40 | − Effect.acquireRelease( | |
| 41 | − createSqlControlPlanePersistence({ | |
| 42 | − localDataDir: ":memory:", | |
| 43 | − }), | |
| 44 | − (persistence) => | |
| 45 | − Effect.tryPromise({ | |
| 46 | − try: () => persistence.close(), | |
| 47 | − catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))), | |
| 48 | − }).pipe(Effect.orDie), | |
| 49 | − ); | |
| 50 | − | |
| 51 | −const encodeSessionDataJson = Schema.encodeSync(McpSourceAuthSessionDataJsonSchema); | |
| 52 | − | |
| 53 | −const makeRuntimeLocalWorkspace = (input: { | |
| 54 | − workspaceId: Source["workspaceId"]; | |
| 55 | − accountId: ReturnType<typeof AccountIdSchema.make>; | |
| 56 | −}) => | |
| 57 | − Effect.gen(function* () { | |
| 58 | − const workspaceRoot = mkdtempSync(join(tmpdir(), "executor-source-store-")); | |
| 59 | − const context = yield* resolveLocalWorkspaceContext({ workspaceRoot }); | |
| 60 | − const loadedConfig = yield* loadLocalExecutorConfig(context); | |
| 61 | − | |
| 62 | − return { | |
| 63 | − context, | |
| 64 | − installation: { | |
| 65 | − workspaceId: input.workspaceId, | |
| 66 | − accountId: input.accountId, | |
| 67 | − }, | |
| 68 | − loadedConfig, | |
| 69 | − } satisfies RuntimeLocalWorkspaceState; | |
| 70 | − }); | |
| 71 | − | |
| 72 | −const withRuntimeLocalWorkspace = <A, E>( | |
| 73 | − effect: Effect.Effect<A, E, never>, | |
| 74 | − runtimeLocalWorkspace: RuntimeLocalWorkspaceState, | |
| 75 | −) => | |
| 76 | − effect.pipe( | |
| 77 | − Effect.provideService(RuntimeLocalWorkspaceService, runtimeLocalWorkspace), | |
| 78 | − ); | |
| 79 | − | |
| 80 | −const makeOpenApiSource = (input: { | |
| 81 | − workspaceId: Source["workspaceId"]; | |
| 82 | − sourceId: Source["id"]; | |
| 83 | − now: number; | |
| 84 | − updatedAt?: number; | |
| 85 | − name?: string; | |
| 86 | − endpoint?: string; | |
| 87 | − specUrl?: string; | |
| 88 | − auth: Source["auth"]; | |
| 89 | −}): Source => ({ | |
| 90 | − id: input.sourceId, | |
| 91 | − workspaceId: input.workspaceId, | |
| 92 | − name: input.name ?? "GitHub", | |
| 93 | − kind: "openapi", | |
| 94 | − endpoint: input.endpoint ?? "https://api.github.com", | |
| 95 | − status: "connected", | |
| 96 | − enabled: true, | |
| 97 | − namespace: "github", | |
| 98 | − bindingVersion: 1, | |
| 99 | − binding: { | |
| 100 | − specUrl: input.specUrl ?? "https://example.com/openapi.json", | |
| 101 | − defaultHeaders: null, | |
| 102 | − }, | |
| 103 | − importAuthPolicy: "reuse_runtime", | |
| 104 | − importAuth: { kind: "none" }, | |
| 105 | − auth: input.auth, | |
| 106 | − sourceHash: null, | |
| 107 | − lastError: null, | |
| 108 | − createdAt: input.now, | |
| 109 | − updatedAt: input.updatedAt ?? input.now, | |
| 110 | −}); | |
| 111 | − | |
| 112 | −describe("source-store", () => { | |
| 113 | − it.scoped("replaces superseded secrets and removes source auth state cleanly", () => | |
| 114 | − Effect.gen(function* () { | |
| 115 | − const persistence = yield* makePersistence; | |
| 116 | − const now = Date.now(); | |
| 117 | − const accountId = AccountIdSchema.make("acc_source_store"); | |
| 118 | − const workspaceId = WorkspaceIdSchema.make("ws_source_store"); | |
| 119 | − const sourceId = SourceIdSchema.make("github"); | |
| 120 | − const firstTokenId = SecretMaterialIdSchema.make("sec_source_store_first"); | |
| 121 | − const secondTokenId = SecretMaterialIdSchema.make("sec_source_store_second"); | |
| 122 | − const runtimeLocalWorkspace = yield* makeRuntimeLocalWorkspace({ | |
| 123 | − workspaceId, | |
| 124 | − accountId, | |
| 125 | − }); | |
| 126 | − | |
| 127 | − yield* persistence.rows.secretMaterials.upsert({ | |
| 128 | − id: firstTokenId, | |
| 129 | − name: null, | |
| 130 | − purpose: "auth_material", | |
| 131 | − value: "ghp_first", | |
| 132 | − createdAt: now, | |
| 133 | − updatedAt: now, | |
| 134 | − }); | |
| 135 | − yield* persistence.rows.secretMaterials.upsert({ | |
| 136 | − id: secondTokenId, | |
| 137 | − name: null, | |
| 138 | − purpose: "auth_material", | |
| 139 | − value: "ghp_second", | |
| 140 | − createdAt: now, | |
| 141 | − updatedAt: now, | |
| 142 | − }); | |
| 143 | − | |
| 144 | − yield* withRuntimeLocalWorkspace( | |
| 145 | − persistSource( | |
| 146 | − persistence.rows, | |
| 147 | − makeOpenApiSource({ | |
| 148 | − workspaceId, | |
| 149 | − sourceId, | |
| 150 | − now, | |
| 151 | − auth: { | |
| 152 | − kind: "bearer", | |
| 153 | − headerName: "Authorization", | |
| 154 | − prefix: "Bearer ", | |
| 155 | − token: { | |
| 156 | − providerId: "postgres", | |
| 157 | − handle: firstTokenId, | |
| 158 | − }, | |
| 159 | − }, | |
| 160 | − }), | |
| 161 | − ), | |
| 162 | − runtimeLocalWorkspace, | |
| 163 | − ); | |
| 164 | − | |
| 165 | − yield* persistence.rows.sourceAuthSessions.upsert({ | |
| 166 | − id: SourceAuthSessionIdSchema.make("src_auth_source_store"), | |
| 167 | − workspaceId, | |
| 168 | − sourceId, | |
| 169 | − actorAccountId: accountId, | |
| 170 | − executionId: null, | |
| 171 | − interactionId: null, | |
| 172 | − providerKind: "mcp_oauth", | |
| 173 | − credentialSlot: "runtime", | |
| 174 | − status: "pending", | |
| 175 | − state: "state_source_store", | |
| 176 | − sessionDataJson: encodeSessionDataJson({ | |
| 177 | − kind: "mcp_oauth", | |
| 178 | − endpoint: "https://api.github.com", | |
| 179 | − redirectUri: "http://127.0.0.1/callback", | |
| 180 | − scope: null, | |
| 181 | − resourceMetadataUrl: null, | |
| 182 | − authorizationServerUrl: null, | |
| 183 | − resourceMetadata: null, | |
| 184 | − authorizationServerMetadata: null, | |
| 185 | − clientInformation: null, | |
| 186 | − codeVerifier: "verifier", | |
| 187 | − authorizationUrl: "https://example.com/auth", | |
| 188 | − }), | |
| 189 | − errorText: null, | |
| 190 | − completedAt: null, | |
| 191 | − createdAt: now, | |
| 192 | − updatedAt: now, | |
| 193 | − }); | |
| 194 | − | |
| 195 | − yield* withRuntimeLocalWorkspace( | |
| 196 | − persistSource( | |
| 197 | − persistence.rows, | |
| 198 | − makeOpenApiSource({ | |
| 199 | − workspaceId, | |
| 200 | − sourceId, | |
| 201 | − now, | |
| 202 | − updatedAt: now + 1, | |
| 203 | − auth: { | |
| 204 | − kind: "bearer", | |
| 205 | − headerName: "Authorization", | |
| 206 | − prefix: "Bearer ", | |
| 207 | − token: { | |
| 208 | − providerId: "postgres", | |
| 209 | − handle: secondTokenId, | |
| 210 | − }, | |
| 211 | − }, | |
| 212 | − }), | |
| 213 | − ), | |
| 214 | − runtimeLocalWorkspace, | |
| 215 | − ); | |
| 216 | − | |
| 217 | − expect(Option.isNone(yield* persistence.rows.secretMaterials.getById(firstTokenId))).toBe(true); | |
| 218 | − const authArtifacts = yield* persistence.rows.authArtifacts.listByWorkspaceId(workspaceId); | |
| 219 | − expect(authArtifacts).toHaveLength(1); | |
| 220 | − const decoded = authArtifacts[0] ? decodeBuiltInAuthArtifactConfig(authArtifacts[0]) : null; | |
| 221 | − expect( | |
| 222 | − decoded !== null && decoded.artifactKind === "static_bearer" | |
| 223 | − ? decoded.config.token.handle | |
| 224 | − : null, | |
| 225 | − ).toBe(secondTokenId); | |
| 226 | − | |
| 227 | − const removed = yield* withRuntimeLocalWorkspace( | |
| 228 | − removeSourceById(persistence.rows, { | |
| 229 | − workspaceId, | |
| 230 | − sourceId, | |
| 231 | − }), | |
| 232 | − runtimeLocalWorkspace, | |
| 233 | − ); | |
| 234 | − expect(removed).toBe(true); | |
| 235 | − | |
| 236 | − expect(Option.isNone(yield* persistence.rows.secretMaterials.getById(secondTokenId))).toBe(true); | |
| 237 | − expect(yield* persistence.rows.authArtifacts.listByWorkspaceId(workspaceId)).toHaveLength(0); | |
| 238 | − expect(yield* persistence.rows.sourceAuthSessions.listByWorkspaceId(workspaceId)).toHaveLength(0); | |
| 239 | − const config = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 240 | − expect(config.config?.sources?.[sourceId]).toBeUndefined(); | |
| 241 | − const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 242 | − expect(workspaceState.sources[sourceId]).toBeUndefined(); | |
| 243 | − }), | |
| 244 | − ); | |
| 245 | − | |
| 246 | − it.scoped("creates a fresh actor-scoped credential when a shared credential already exists", () => | |
| 247 | − Effect.gen(function* () { | |
| 248 | − const persistence = yield* makePersistence; | |
| 249 | − const now = Date.now(); | |
| 250 | − const accountId = AccountIdSchema.make("acc_actor_scoped"); | |
| 251 | − const workspaceId = WorkspaceIdSchema.make("ws_actor_scoped"); | |
| 252 | − const sourceId = SourceIdSchema.make("github"); | |
| 253 | − const sharedTokenId = SecretMaterialIdSchema.make("sec_actor_scoped_shared"); | |
| 254 | − const runtimeLocalWorkspace = yield* makeRuntimeLocalWorkspace({ | |
| 255 | − workspaceId, | |
| 256 | − accountId, | |
| 257 | − }); | |
| 258 | − yield* persistence.rows.secretMaterials.upsert({ | |
| 259 | − id: sharedTokenId, | |
| 260 | − name: null, | |
| 261 | − purpose: "auth_material", | |
| 262 | − value: "ghp_shared", | |
| 263 | − createdAt: now, | |
| 264 | − updatedAt: now, | |
| 265 | − }); | |
| 266 | − | |
| 267 | − const source = makeOpenApiSource({ | |
| 268 | − workspaceId, | |
| 269 | − sourceId, | |
| 270 | − now, | |
| 271 | − auth: { | |
| 272 | − kind: "bearer", | |
| 273 | − headerName: "Authorization", | |
| 274 | − prefix: "Bearer ", | |
| 275 | − token: { | |
| 276 | − providerId: "postgres", | |
| 277 | − handle: sharedTokenId, | |
| 278 | − }, | |
| 279 | − }, | |
| 280 | − }); | |
| 281 | − | |
| 282 | − yield* withRuntimeLocalWorkspace( | |
| 283 | − persistSource(persistence.rows, source), | |
| 284 | − runtimeLocalWorkspace, | |
| 285 | − ); | |
| 286 | − yield* withRuntimeLocalWorkspace( | |
| 287 | − persistSource( | |
| 288 | − persistence.rows, | |
| 289 | − { | |
| 290 | − ...source, | |
| 291 | − updatedAt: now + 1, | |
| 292 | − }, | |
| 293 | − { | |
| 294 | − actorAccountId: accountId, | |
| 295 | − }, | |
| 296 | − ), | |
| 297 | − runtimeLocalWorkspace, | |
| 298 | − ); | |
| 299 | − | |
| 300 | − const authArtifacts = yield* persistence.rows.authArtifacts.listByWorkspaceAndSourceId({ | |
| 301 | − workspaceId, | |
| 302 | − sourceId, | |
| 303 | − }); | |
| 304 | − expect(authArtifacts).toHaveLength(2); | |
| 305 | − expect(authArtifacts.some((artifact) => artifact.actorAccountId === null)).toBe(true); | |
| 306 | − expect(authArtifacts.some((artifact) => artifact.actorAccountId === accountId)).toBe(true); | |
| 307 | − expect(new Set(authArtifacts.map((artifact) => artifact.id)).size).toBe(2); | |
| 308 | − }), | |
| 309 | − ); | |
| 310 | −}); | |
| @@ -9,7 +9,6 @@ import type { · 8 unmodified lines | ||
| 9 | 9 | WorkspaceId, |
| 10 | 10 | } from "#schema"; |
| 11 | 11 | import { SourceIdSchema } from "#schema"; |
| 12 | −import { type SqlControlPlaneRows } from "#persistence"; | |
| 13 | 12 | import * as Effect from "effect/Effect"; |
| 14 | 13 | |
| 15 | 14 | import { |
| @@ -21,15 +20,9 @@ import { · 5 unmodified lines | ||
| 21 | 20 | sourceAuthFromAuthArtifact, |
| 22 | 21 | } from "./auth-artifacts"; |
| 23 | 22 | import { |
| 24 | − loadLocalExecutorConfig, | |
| 25 | 23 | type LoadedLocalExecutorConfig, |
| 26 | 24 | type ResolvedLocalWorkspaceContext, |
| 27 | − writeProjectLocalExecutorConfig, | |
| 28 | 25 | } from "./local-config"; |
| 29 | −import { | |
| 30 | − readLocalSourceArtifact, | |
| 31 | − removeLocalSourceArtifact, | |
| 32 | −} from "./local-source-artifacts"; | |
| 33 | 26 | import { |
| 34 | 27 | LocalConfiguredSourceNotFoundError, |
| 35 | 28 | LocalExecutorConfigDecodeError, |
| @@ -42,9 +35,18 @@ import { · 6 unmodified lines | ||
| 42 | 35 | import { |
| 43 | 36 | requireRuntimeLocalWorkspace, |
| 44 | 37 | } from "./local-runtime-context"; |
| 38 | +import type { | |
| 39 | + SourceArtifactStoreShape, | |
| 40 | + WorkspaceStorageServices, | |
| 41 | + WorkspaceConfigStoreShape, | |
| 42 | + WorkspaceStateStoreShape, | |
| 43 | +} from "./local-storage"; | |
| 44 | +import { | |
| 45 | + SourceArtifactStore, | |
| 46 | + WorkspaceConfigStore, | |
| 47 | + WorkspaceStateStore, | |
| 48 | +} from "./local-storage"; | |
| 45 | 49 | import { |
| 46 | − loadLocalWorkspaceState, | |
| 47 | − writeLocalWorkspaceState, | |
| 48 | 50 | type LocalWorkspaceState, |
| 49 | 51 | } from "./local-workspace-state"; |
| 50 | 52 | import { |
| @@ -54,13 +56,14 @@ import { · 3 unmodified lines | ||
| 54 | 56 | import { createDefaultSecretMaterialDeleter } from "./secret-material-providers"; |
| 55 | 57 | import { authArtifactSecretMaterialRefs } from "./auth-artifacts"; |
| 56 | 58 | import { removeAuthLeaseAndSecrets } from "./auth-leases"; |
| 59 | +import type { ControlPlaneStoreShape } from "./store"; | |
| 57 | 60 | import { getSourceAdapter } from "./source-adapters"; |
| 58 | 61 | import { slugify } from "./slug"; |
| 59 | 62 | |
| 60 | 63 | const secretRefKey = (ref: { providerId: string; handle: string }): string => |
| 61 | 64 | `${ref.providerId}:${ref.handle}`; |
| 62 | 65 | |
| 63 | −const cleanupAuthArtifactSecretRefs = (rows: SqlControlPlaneRows, input: { | |
| 66 | +const cleanupAuthArtifactSecretRefs = (rows: ControlPlaneStoreShape, input: { | |
| 64 | 67 | previous: AuthArtifact | null; |
| 65 | 68 | next: AuthArtifact | null; |
| 66 | 69 | }) => |
| @@ -246,6 +249,9 @@ const resolveRuntimeLocalWorkspace = (workspaceId: WorkspaceId): Effect.Effect<{ · 179 unmodified lines | ||
| 246 | 249 | workspaceId: WorkspaceId; |
| 247 | 250 | accountId: AccountId; |
| 248 | 251 | }; |
| 252 | + workspaceConfigStore: WorkspaceConfigStoreShape; | |
| 253 | + workspaceStateStore: WorkspaceStateStoreShape; | |
| 254 | + sourceArtifactStore: SourceArtifactStoreShape; | |
| 249 | 255 | loadedConfig: LoadedLocalExecutorConfig; |
| 250 | 256 | workspaceState: LocalWorkspaceState; |
| 251 | 257 | }, |
| @@ -253,16 +259,27 @@ const resolveRuntimeLocalWorkspace = (workspaceId: WorkspaceId): Effect.Effect<{ · 1 unmodified line | ||
| 253 | 259 | | RuntimeLocalWorkspaceMismatchError |
| 254 | 260 | | LocalFileSystemError |
| 255 | 261 | | LocalExecutorConfigDecodeError |
| 256 | − | LocalWorkspaceStateDecodeError, | |
| 257 | −never> => | |
| 262 | + | LocalWorkspaceStateDecodeError | |
| 263 | + | Error, | |
| 264 | +WorkspaceStorageServices> => | |
| 258 | 265 | Effect.gen(function* () { |
| 259 | 266 | const runtimeLocalWorkspace = yield* requireRuntimeLocalWorkspace(workspaceId); |
| 260 | − const loadedConfig = yield* loadLocalExecutorConfig(runtimeLocalWorkspace.context); | |
| 261 | − const workspaceState = yield* loadLocalWorkspaceState(runtimeLocalWorkspace.context); | |
| 267 | + const workspaceConfigStore = yield* WorkspaceConfigStore; | |
| 268 | + const workspaceStateStore = yield* WorkspaceStateStore; | |
| 269 | + const sourceArtifactStore = yield* SourceArtifactStore; | |
| 270 | + const loadedConfig = yield* workspaceConfigStore.load( | |
| 271 | + runtimeLocalWorkspace.context, | |
| 272 | + ); | |
| 273 | + const workspaceState = yield* workspaceStateStore.load( | |
| 274 | + runtimeLocalWorkspace.context, | |
| 275 | + ); | |
| 262 | 276 | |
| 263 | 277 | return { |
| 264 | 278 | context: runtimeLocalWorkspace.context, |
| 265 | 279 | installation: runtimeLocalWorkspace.installation, |
| 280 | + workspaceConfigStore, | |
| 281 | + workspaceStateStore, | |
| 282 | + sourceArtifactStore, | |
| 266 | 283 | loadedConfig, |
| 267 | 284 | workspaceState, |
| 268 | 285 | }; |
| @@ -270,6 +287,7 @@ never> => · 1 unmodified line | ||
| 270 | 287 | |
| 271 | 288 | const buildLocalSourceRecord = (input: { |
| 272 | 289 | context: ResolvedLocalWorkspaceContext; |
| 290 | + sourceArtifactStore: SourceArtifactStoreShape; | |
| 273 | 291 | workspaceId: WorkspaceId; |
| 274 | 292 | loadedConfig: LoadedLocalExecutorConfig; |
| 275 | 293 | workspaceState: LocalWorkspaceState; |
| @@ -317,7 +335,7 @@ const buildLocalSourceRecord = (input: { · 41 unmodified lines | ||
| 317 | 335 | updatedAt: existingState?.updatedAt ?? Date.now(), |
| 318 | 336 | }); |
| 319 | 337 | |
| 320 | − const artifact = yield* readLocalSourceArtifact({ | |
| 338 | + const artifact = yield* input.sourceArtifactStore.read({ | |
| 321 | 339 | context: input.context, |
| 322 | 340 | sourceId: input.sourceId, |
| 323 | 341 | }).pipe( |
| @@ -379,7 +397,7 @@ const buildLocalSourceRecord = (input: { · 55 unmodified lines | ||
| 379 | 397 | }); |
| 380 | 398 | |
| 381 | 399 | export const loadSourcesInWorkspace = ( |
| 382 | − rows: SqlControlPlaneRows, | |
| 400 | + rows: ControlPlaneStoreShape, | |
| 383 | 401 | workspaceId: WorkspaceId, |
| 384 | 402 | options: { |
| 385 | 403 | actorAccountId?: AccountId | null; |
| @@ -393,7 +411,7 @@ export const loadSourcesInWorkspace = ( · 7 unmodified lines | ||
| 393 | 411 | | LocalWorkspaceStateDecodeError |
| 394 | 412 | | LocalConfiguredSourceNotFoundError |
| 395 | 413 | | Error, |
| 396 | − never | |
| 414 | + WorkspaceStorageServices | |
| 397 | 415 | > => |
| 398 | 416 | Effect.gen(function* () { |
| 399 | 417 | const localWorkspace = yield* resolveRuntimeLocalWorkspace(workspaceId); |
| @@ -404,6 +422,7 @@ export const loadSourcesInWorkspace = ( · 4 unmodified lines | ||
| 404 | 422 | Effect.map( |
| 405 | 423 | buildLocalSourceRecord({ |
| 406 | 424 | context: localWorkspace.context, |
| 425 | + sourceArtifactStore: localWorkspace.sourceArtifactStore, | |
| 407 | 426 | workspaceId, |
| 408 | 427 | loadedConfig: localWorkspace.loadedConfig, |
| 409 | 428 | workspaceState: localWorkspace.workspaceState, |
| @@ -416,7 +435,7 @@ export const loadSourcesInWorkspace = ( · 6 unmodified lines | ||
| 416 | 435 | ); |
| 417 | 436 | }); |
| 418 | 437 | |
| 419 | −export const loadSourceById = (rows: SqlControlPlaneRows, input: { | |
| 438 | +export const loadSourceById = (rows: ControlPlaneStoreShape, input: { | |
| 420 | 439 | workspaceId: WorkspaceId; |
| 421 | 440 | sourceId: Source["id"]; |
| 422 | 441 | actorAccountId?: AccountId | null; |
| @@ -429,7 +448,7 @@ export const loadSourceById = (rows: SqlControlPlaneRows, input: { · 6 unmodified lines | ||
| 429 | 448 | | LocalWorkspaceStateDecodeError |
| 430 | 449 | | LocalConfiguredSourceNotFoundError |
| 431 | 450 | | Error, |
| 432 | − never | |
| 451 | + WorkspaceStorageServices | |
| 433 | 452 | > => |
| 434 | 453 | Effect.gen(function* () { |
| 435 | 454 | const localWorkspace = yield* resolveRuntimeLocalWorkspace(input.workspaceId); |
| @@ -445,6 +464,7 @@ export const loadSourceById = (rows: SqlControlPlaneRows, input: { · 9 unmodified lines | ||
| 445 | 464 | |
| 446 | 465 | const localSource = yield* buildLocalSourceRecord({ |
| 447 | 466 | context: localWorkspace.context, |
| 467 | + sourceArtifactStore: localWorkspace.sourceArtifactStore, | |
| 448 | 468 | workspaceId: input.workspaceId, |
| 449 | 469 | loadedConfig: localWorkspace.loadedConfig, |
| 450 | 470 | workspaceState: localWorkspace.workspaceState, |
| @@ -514,7 +534,7 @@ const configSourceFromLocalSource = (input: { · 63 unmodified lines | ||
| 514 | 534 | } |
| 515 | 535 | }; |
| 516 | 536 | |
| 517 | −const removeAuthArtifactsForSource = (rows: SqlControlPlaneRows, input: { | |
| 537 | +const removeAuthArtifactsForSource = (rows: ControlPlaneStoreShape, input: { | |
| 518 | 538 | workspaceId: WorkspaceId; |
| 519 | 539 | sourceId: Source["id"]; |
| 520 | 540 | }) => |
| @@ -551,10 +571,10 @@ const removeAuthArtifactsForSource = (rows: SqlControlPlaneRows, input: { · 30 unmodified lines | ||
| 551 | 571 | return existingAuthArtifacts.length; |
| 552 | 572 | }); |
| 553 | 573 | |
| 554 | −export const removeSourceById = (rows: SqlControlPlaneRows, input: { | |
| 574 | +export const removeSourceById = (rows: ControlPlaneStoreShape, input: { | |
| 555 | 575 | workspaceId: WorkspaceId; |
| 556 | 576 | sourceId: Source["id"]; |
| 557 | −}) => | |
| 577 | +}): Effect.Effect<boolean, Error, WorkspaceStorageServices> => | |
| 558 | 578 | Effect.gen(function* () { |
| 559 | 579 | const localWorkspace = yield* resolveRuntimeLocalWorkspace(input.workspaceId); |
| 560 | 580 | if (!localWorkspace.loadedConfig.config?.sources?.[input.sourceId]) { |
| @@ -566,7 +586,7 @@ export const removeSourceById = (rows: SqlControlPlaneRows, input: { · 5 unmodified lines | ||
| 566 | 586 | ...(projectConfig.sources ?? {}), |
| 567 | 587 | }; |
| 568 | 588 | delete sources[input.sourceId]; |
| 569 | − yield* writeProjectLocalExecutorConfig({ | |
| 589 | + yield* localWorkspace.workspaceConfigStore.writeProject({ | |
| 570 | 590 | context: localWorkspace.context, |
| 571 | 591 | config: { |
| 572 | 592 | ...projectConfig, |
| @@ -582,11 +602,11 @@ export const removeSourceById = (rows: SqlControlPlaneRows, input: { · 9 unmodified lines | ||
| 582 | 602 | ...localWorkspace.workspaceState, |
| 583 | 603 | sources: remainingSources, |
| 584 | 604 | }; |
| 585 | − yield* writeLocalWorkspaceState({ | |
| 605 | + yield* localWorkspace.workspaceStateStore.write({ | |
| 586 | 606 | context: localWorkspace.context, |
| 587 | 607 | state: workspaceState, |
| 588 | 608 | }); |
| 589 | − yield* removeLocalSourceArtifact({ | |
| 609 | + yield* localWorkspace.sourceArtifactStore.remove({ | |
| 590 | 610 | context: localWorkspace.context, |
| 591 | 611 | sourceId: input.sourceId, |
| 592 | 612 | }); |
| @@ -605,12 +625,12 @@ export const removeSourceById = (rows: SqlControlPlaneRows, input: { · 12 unmodified lines | ||
| 605 | 625 | }); |
| 606 | 626 | |
| 607 | 627 | export const persistSource = ( |
| 608 | − rows: SqlControlPlaneRows, | |
| 628 | + rows: ControlPlaneStoreShape, | |
| 609 | 629 | source: Source, |
| 610 | 630 | options: { |
| 611 | 631 | actorAccountId?: AccountId | null; |
| 612 | 632 | } = {}, |
| 613 | −) => | |
| 633 | +): Effect.Effect<Source, Error, WorkspaceStorageServices> => | |
| 614 | 634 | Effect.gen(function* () { |
| 615 | 635 | const localWorkspace = yield* resolveRuntimeLocalWorkspace(source.workspaceId); |
| 616 | 636 | const nextSource = { |
| @@ -648,7 +668,7 @@ export const persistSource = ( · 31 unmodified lines | ||
| 648 | 668 | existingConfigAuth: existingConfigSource?.connection.auth, |
| 649 | 669 | config: localWorkspace.loadedConfig.config, |
| 650 | 670 | }); |
| 651 | − yield* writeProjectLocalExecutorConfig({ | |
| 671 | + yield* localWorkspace.workspaceConfigStore.writeProject({ | |
| 652 | 672 | context: localWorkspace.context, |
| 653 | 673 | config: { |
| 654 | 674 | ...projectConfig, |
| @@ -737,7 +757,7 @@ export const persistSource = ( · 82 unmodified lines | ||
| 737 | 757 | }, |
| 738 | 758 | }, |
| 739 | 759 | }; |
| 740 | − yield* writeLocalWorkspaceState({ | |
| 760 | + yield* localWorkspace.workspaceStateStore.write({ | |
| 741 | 761 | context: localWorkspace.context, |
| 742 | 762 | state: workspaceState, |
| 743 | 763 | }); |
| @@ -1,15 +1,205 @@ | ||
| 1 | −import { type SqlControlPlaneRows, SqlControlPlaneRowsService } from "#persistence"; | |
| 2 | 1 | import * as Context from "effect/Context"; |
| 3 | −import * as Effect from "effect/Effect"; | |
| 4 | −import * as Layer from "effect/Layer"; | |
| 2 | +import type * as Effect from "effect/Effect"; | |
| 5 | 3 | |
| 6 | −export type ControlPlaneStoreShape = SqlControlPlaneRows; | |
| 4 | +import type { | |
| 5 | + AuthArtifact, | |
| 6 | + AuthLease, | |
| 7 | + Execution, | |
| 8 | + ExecutionInteraction, | |
| 9 | + ExecutionStep, | |
| 10 | + SecretMaterial, | |
| 11 | + SourceAuthSession, | |
| 12 | + WorkspaceSourceOauthClient, | |
| 13 | +} from "#schema"; | |
| 14 | + | |
| 15 | +type SecretMaterialSummary = { | |
| 16 | + id: string; | |
| 17 | + name: string | null; | |
| 18 | + purpose: string; | |
| 19 | + createdAt: number; | |
| 20 | + updatedAt: number; | |
| 21 | +}; | |
| 22 | + | |
| 23 | +export type ControlPlaneStoreShape = { | |
| 24 | + authArtifacts: { | |
| 25 | + listByWorkspaceId: ( | |
| 26 | + workspaceId: AuthArtifact["workspaceId"], | |
| 27 | + ) => Effect.Effect<readonly AuthArtifact[], Error, never>; | |
| 28 | + listByWorkspaceAndSourceId: (input: { | |
| 29 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 30 | + sourceId: AuthArtifact["sourceId"]; | |
| 31 | + }) => Effect.Effect<readonly AuthArtifact[], Error, never>; | |
| 32 | + getByWorkspaceSourceAndActor: (input: { | |
| 33 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 34 | + sourceId: AuthArtifact["sourceId"]; | |
| 35 | + actorAccountId: AuthArtifact["actorAccountId"]; | |
| 36 | + slot: AuthArtifact["slot"]; | |
| 37 | + }) => Effect.Effect<import("effect/Option").Option<AuthArtifact>, Error, never>; | |
| 38 | + upsert: (artifact: AuthArtifact) => Effect.Effect<void, Error, never>; | |
| 39 | + removeByWorkspaceSourceAndActor: (input: { | |
| 40 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 41 | + sourceId: AuthArtifact["sourceId"]; | |
| 42 | + actorAccountId: AuthArtifact["actorAccountId"]; | |
| 43 | + slot?: AuthArtifact["slot"]; | |
| 44 | + }) => Effect.Effect<boolean, Error, never>; | |
| 45 | + removeByWorkspaceAndSourceId: (input: { | |
| 46 | + workspaceId: AuthArtifact["workspaceId"]; | |
| 47 | + sourceId: AuthArtifact["sourceId"]; | |
| 48 | + }) => Effect.Effect<number, Error, never>; | |
| 49 | + }; | |
| 50 | + authLeases: { | |
| 51 | + listAll: () => Effect.Effect<readonly AuthLease[], Error, never>; | |
| 52 | + getByAuthArtifactId: ( | |
| 53 | + authArtifactId: AuthLease["authArtifactId"], | |
| 54 | + ) => Effect.Effect<import("effect/Option").Option<AuthLease>, Error, never>; | |
| 55 | + upsert: (lease: AuthLease) => Effect.Effect<void, Error, never>; | |
| 56 | + removeByAuthArtifactId: ( | |
| 57 | + authArtifactId: AuthLease["authArtifactId"], | |
| 58 | + ) => Effect.Effect<boolean, Error, never>; | |
| 59 | + }; | |
| 60 | + sourceOauthClients: { | |
| 61 | + getByWorkspaceSourceAndProvider: (input: { | |
| 62 | + workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 63 | + sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 64 | + providerKey: string; | |
| 65 | + }) => Effect.Effect< | |
| 66 | + import("effect/Option").Option<WorkspaceSourceOauthClient>, | |
| 67 | + Error, | |
| 68 | + never | |
| 69 | + >; | |
| 70 | + upsert: ( | |
| 71 | + oauthClient: WorkspaceSourceOauthClient, | |
| 72 | + ) => Effect.Effect<void, Error, never>; | |
| 73 | + removeByWorkspaceAndSourceId: (input: { | |
| 74 | + workspaceId: WorkspaceSourceOauthClient["workspaceId"]; | |
| 75 | + sourceId: WorkspaceSourceOauthClient["sourceId"]; | |
| 76 | + }) => Effect.Effect<number, Error, never>; | |
| 77 | + }; | |
| 78 | + sourceAuthSessions: { | |
| 79 | + listAll: () => Effect.Effect<readonly SourceAuthSession[], Error, never>; | |
| 80 | + listByWorkspaceId: ( | |
| 81 | + workspaceId: SourceAuthSession["workspaceId"], | |
| 82 | + ) => Effect.Effect<readonly SourceAuthSession[], Error, never>; | |
| 83 | + getById: ( | |
| 84 | + id: SourceAuthSession["id"], | |
| 85 | + ) => Effect.Effect<import("effect/Option").Option<SourceAuthSession>, Error, never>; | |
| 86 | + getByState: ( | |
| 87 | + state: SourceAuthSession["state"], | |
| 88 | + ) => Effect.Effect<import("effect/Option").Option<SourceAuthSession>, Error, never>; | |
| 89 | + getPendingByWorkspaceSourceAndActor: (input: { | |
| 90 | + workspaceId: SourceAuthSession["workspaceId"]; | |
| 91 | + sourceId: SourceAuthSession["sourceId"]; | |
| 92 | + actorAccountId: SourceAuthSession["actorAccountId"]; | |
| 93 | + credentialSlot?: SourceAuthSession["credentialSlot"]; | |
| 94 | + }) => Effect.Effect< | |
| 95 | + import("effect/Option").Option<SourceAuthSession>, | |
| 96 | + Error, | |
| 97 | + never | |
| 98 | + >; | |
| 99 | + insert: (session: SourceAuthSession) => Effect.Effect<void, Error, never>; | |
| 100 | + update: ( | |
| 101 | + id: SourceAuthSession["id"], | |
| 102 | + patch: Partial< | |
| 103 | + Omit<SourceAuthSession, "id" | "workspaceId" | "sourceId" | "createdAt"> | |
| 104 | + >, | |
| 105 | + ) => Effect.Effect<import("effect/Option").Option<SourceAuthSession>, Error, never>; | |
| 106 | + upsert: (session: SourceAuthSession) => Effect.Effect<void, Error, never>; | |
| 107 | + removeByWorkspaceAndSourceId: ( | |
| 108 | + workspaceId: SourceAuthSession["workspaceId"], | |
| 109 | + sourceId: SourceAuthSession["sourceId"], | |
| 110 | + ) => Effect.Effect<boolean, Error, never>; | |
| 111 | + }; | |
| 112 | + secretMaterials: { | |
| 113 | + getById: ( | |
| 114 | + id: SecretMaterial["id"], | |
| 115 | + ) => Effect.Effect<import("effect/Option").Option<SecretMaterial>, Error, never>; | |
| 116 | + listAll: () => Effect.Effect<readonly SecretMaterialSummary[], Error, never>; | |
| 117 | + upsert: (material: SecretMaterial) => Effect.Effect<void, Error, never>; | |
| 118 | + updateById: ( | |
| 119 | + id: SecretMaterial["id"], | |
| 120 | + update: { name?: string | null; value?: string }, | |
| 121 | + ) => Effect.Effect< | |
| 122 | + import("effect/Option").Option<SecretMaterialSummary>, | |
| 123 | + Error, | |
| 124 | + never | |
| 125 | + >; | |
| 126 | + removeById: (id: SecretMaterial["id"]) => Effect.Effect<boolean, Error, never>; | |
| 127 | + listLinkedSources: () => Effect.Effect< | |
| 128 | + Map<string, Array<{ sourceId: string; sourceName: string }>>, | |
| 129 | + Error, | |
| 130 | + never | |
| 131 | + >; | |
| 132 | + }; | |
| 133 | + executions: { | |
| 134 | + getById: ( | |
| 135 | + executionId: Execution["id"], | |
| 136 | + ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>; | |
| 137 | + getByWorkspaceAndId: ( | |
| 138 | + workspaceId: Execution["workspaceId"], | |
| 139 | + executionId: Execution["id"], | |
| 140 | + ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>; | |
| 141 | + insert: (execution: Execution) => Effect.Effect<void, Error, never>; | |
| 142 | + update: ( | |
| 143 | + executionId: Execution["id"], | |
| 144 | + patch: Partial< | |
| 145 | + Omit<Execution, "id" | "workspaceId" | "createdByAccountId" | "createdAt"> | |
| 146 | + >, | |
| 147 | + ) => Effect.Effect<import("effect/Option").Option<Execution>, Error, never>; | |
| 148 | + }; | |
| 149 | + executionInteractions: { | |
| 150 | + getById: ( | |
| 151 | + interactionId: ExecutionInteraction["id"], | |
| 152 | + ) => Effect.Effect< | |
| 153 | + import("effect/Option").Option<ExecutionInteraction>, | |
| 154 | + Error, | |
| 155 | + never | |
| 156 | + >; | |
| 157 | + listByExecutionId: ( | |
| 158 | + executionId: ExecutionInteraction["executionId"], | |
| 159 | + ) => Effect.Effect<readonly ExecutionInteraction[], Error, never>; | |
| 160 | + getPendingByExecutionId: ( | |
| 161 | + executionId: ExecutionInteraction["executionId"], | |
| 162 | + ) => Effect.Effect< | |
| 163 | + import("effect/Option").Option<ExecutionInteraction>, | |
| 164 | + Error, | |
| 165 | + never | |
| 166 | + >; | |
| 167 | + insert: ( | |
| 168 | + interaction: ExecutionInteraction, | |
| 169 | + ) => Effect.Effect<void, Error, never>; | |
| 170 | + update: ( | |
| 171 | + interactionId: ExecutionInteraction["id"], | |
| 172 | + patch: Partial< | |
| 173 | + Omit<ExecutionInteraction, "id" | "executionId" | "createdAt"> | |
| 174 | + >, | |
| 175 | + ) => Effect.Effect< | |
| 176 | + import("effect/Option").Option<ExecutionInteraction>, | |
| 177 | + Error, | |
| 178 | + never | |
| 179 | + >; | |
| 180 | + }; | |
| 181 | + executionSteps: { | |
| 182 | + getByExecutionAndSequence: ( | |
| 183 | + executionId: ExecutionStep["executionId"], | |
| 184 | + sequence: ExecutionStep["sequence"], | |
| 185 | + ) => Effect.Effect<import("effect/Option").Option<ExecutionStep>, Error, never>; | |
| 186 | + listByExecutionId: ( | |
| 187 | + executionId: ExecutionStep["executionId"], | |
| 188 | + ) => Effect.Effect<readonly ExecutionStep[], Error, never>; | |
| 189 | + insert: (step: ExecutionStep) => Effect.Effect<void, Error, never>; | |
| 190 | + deleteByExecutionId: ( | |
| 191 | + executionId: ExecutionStep["executionId"], | |
| 192 | + ) => Effect.Effect<void, Error, never>; | |
| 193 | + updateByExecutionAndSequence: ( | |
| 194 | + executionId: ExecutionStep["executionId"], | |
| 195 | + sequence: ExecutionStep["sequence"], | |
| 196 | + patch: Partial< | |
| 197 | + Omit<ExecutionStep, "id" | "executionId" | "sequence" | "createdAt"> | |
| 198 | + >, | |
| 199 | + ) => Effect.Effect<import("effect/Option").Option<ExecutionStep>, Error, never>; | |
| 200 | + }; | |
| 201 | +}; | |
| 7 | 202 | |
| 8 | 203 | export class ControlPlaneStore extends Context.Tag( |
| 9 | 204 | "#runtime/ControlPlaneStore", |
| 10 | 205 | )<ControlPlaneStore, ControlPlaneStoreShape>() {} |
| 11 | − | |
| 12 | −export const ControlPlaneStoreLive = Layer.effect( | |
| 13 | − ControlPlaneStore, | |
| 14 | − Effect.map(SqlControlPlaneRowsService, (rows) => rows), | |
| 15 | −); | |
| @@ -1,1986 +0,0 @@ | ||
| 1 | −import { randomUUID } from "node:crypto"; | |
| 2 | −import { createServer } from "node:http"; | |
| 3 | −import { dirname, join } from "node:path"; | |
| 4 | − | |
| 5 | −import { | |
| 6 | − FileSystem, | |
| 7 | − HttpApi, | |
| 8 | − HttpApiBuilder, | |
| 9 | − HttpApiEndpoint, | |
| 10 | − HttpApiGroup, | |
| 11 | − HttpApiSchema, | |
| 12 | − HttpServer, | |
| 13 | −} from "@effect/platform"; | |
| 14 | −import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"; | |
| 15 | −import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer"; | |
| 16 | −import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js"; | |
| 17 | −import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | |
| 18 | −import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; | |
| 19 | −import { | |
| 20 | − buildOpenApiToolPresentation, | |
| 21 | − compileOpenApiToolDefinitions, | |
| 22 | − extractOpenApiManifest, | |
| 23 | −} from "@executor/codemode-openapi"; | |
| 24 | −import { describe, expect, it } from "@effect/vitest"; | |
| 25 | −import { | |
| 26 | − graphql, | |
| 27 | − GraphQLBoolean, | |
| 28 | − GraphQLID, | |
| 29 | − GraphQLInputObjectType, | |
| 30 | − GraphQLInt, | |
| 31 | − GraphQLList, | |
| 32 | − GraphQLNonNull, | |
| 33 | − GraphQLObjectType, | |
| 34 | − GraphQLSchema, | |
| 35 | − GraphQLString, | |
| 36 | −} from "graphql"; | |
| 37 | −import { | |
| 38 | − AccountIdSchema, | |
| 39 | − ExecutionIdSchema, | |
| 40 | − SecretMaterialIdSchema, | |
| 41 | − SourceIdSchema, | |
| 42 | − SourceRecipeSchemaBundleIdSchema, | |
| 43 | − WorkspaceIdSchema, | |
| 44 | − type Source, | |
| 45 | −} from "#schema"; | |
| 46 | −import * as Context from "effect/Context"; | |
| 47 | −import * as Effect from "effect/Effect"; | |
| 48 | −import * as Exit from "effect/Exit"; | |
| 49 | −import * as Layer from "effect/Layer"; | |
| 50 | −import * as Scope from "effect/Scope"; | |
| 51 | −import { Schema } from "effect"; | |
| 52 | −import * as z from "zod/v4"; | |
| 53 | − | |
| 54 | −import { | |
| 55 | − createSqlControlPlanePersistence, | |
| 56 | − type SqlControlPlanePersistence, | |
| 57 | −} from "#persistence"; | |
| 58 | −import type { SourceId, WorkspaceId } from "#schema"; | |
| 59 | − | |
| 60 | −import { | |
| 61 | − createRuntimeSourceAuthService, | |
| 62 | − type RuntimeSourceAuthService, | |
| 63 | −} from "./source-auth-service"; | |
| 64 | −import { | |
| 65 | − loadLocalExecutorConfig, | |
| 66 | − resolveLocalWorkspaceContext, | |
| 67 | − writeProjectLocalExecutorConfig, | |
| 68 | −} from "./local-config"; | |
| 69 | −import { createLiveExecutionManager } from "./live-execution"; | |
| 70 | −import { | |
| 71 | − RuntimeLocalWorkspaceService, | |
| 72 | − type RuntimeLocalWorkspaceState, | |
| 73 | −} from "./local-runtime-context"; | |
| 74 | −import { | |
| 75 | − buildLocalSourceArtifact, | |
| 76 | − readLocalSourceArtifact, | |
| 77 | − writeLocalSourceArtifact, | |
| 78 | −} from "./local-source-artifacts"; | |
| 79 | −import { | |
| 80 | − loadLocalWorkspaceState, | |
| 81 | − writeLocalWorkspaceState, | |
| 82 | −} from "./local-workspace-state"; | |
| 83 | −import { materializationFromMcpManifestEntries } from "./source-adapters/mcp"; | |
| 84 | −import { createWorkspaceExecutionEnvironmentResolver } from "./workspace-execution-environment"; | |
| 85 | − | |
| 86 | −type CountedMcpServer = { | |
| 87 | − endpoint: string; | |
| 88 | − counts: { | |
| 89 | − listTools: number; | |
| 90 | − callTool: number; | |
| 91 | − }; | |
| 92 | − close: () => Promise<void>; | |
| 93 | −}; | |
| 94 | − | |
| 95 | −type OpenApiSpecServer = { | |
| 96 | − baseUrl: string; | |
| 97 | − specUrl: string; | |
| 98 | − seenAuthHeaders: Array<string | null>; | |
| 99 | − close: () => Promise<void>; | |
| 100 | −}; | |
| 101 | − | |
| 102 | −type GraphqlServer = { | |
| 103 | − endpoint: string; | |
| 104 | − seenAuthHeaders: Array<string | null>; | |
| 105 | − close: () => Promise<void>; | |
| 106 | −}; | |
| 107 | − | |
| 108 | −const normalizeSearchText = ( | |
| 109 | − ...parts: ReadonlyArray<string | null | undefined> | |
| 110 | −): string => | |
| 111 | − parts | |
| 112 | − .flatMap((part) => (part ? [part.trim()] : [])) | |
| 113 | − .filter(Boolean) | |
| 114 | − .join(" ") | |
| 115 | − .toLowerCase(); | |
| 116 | − | |
| 117 | −const provideNodeFileSystem = <A, E, R>( | |
| 118 | − effect: Effect.Effect<A, E, R | FileSystem.FileSystem>, | |
| 119 | −): Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem>> => | |
| 120 | − effect.pipe(Effect.provide(NodeFileSystem.layer)) as Effect.Effect< | |
| 121 | − A, | |
| 122 | − E, | |
| 123 | − Exclude<R, FileSystem.FileSystem> | |
| 124 | − >; | |
| 125 | − | |
| 126 | −const GraphqlUserType = new GraphQLObjectType({ | |
| 127 | − name: "User", | |
| 128 | − fields: { | |
| 129 | − id: { | |
| 130 | − type: new GraphQLNonNull(GraphQLID), | |
| 131 | − }, | |
| 132 | − name: { | |
| 133 | − type: new GraphQLNonNull(GraphQLString), | |
| 134 | − }, | |
| 135 | − }, | |
| 136 | −}); | |
| 137 | − | |
| 138 | −const GraphqlTeamType = new GraphQLObjectType({ | |
| 139 | − name: "Team", | |
| 140 | − fields: { | |
| 141 | − id: { | |
| 142 | − type: new GraphQLNonNull(GraphQLID), | |
| 143 | − }, | |
| 144 | − key: { | |
| 145 | − type: new GraphQLNonNull(GraphQLString), | |
| 146 | − }, | |
| 147 | − name: { | |
| 148 | − type: new GraphQLNonNull(GraphQLString), | |
| 149 | − }, | |
| 150 | − }, | |
| 151 | −}); | |
| 152 | − | |
| 153 | −const GraphqlIssueType = new GraphQLObjectType({ | |
| 154 | − name: "Issue", | |
| 155 | − fields: { | |
| 156 | − id: { | |
| 157 | − type: new GraphQLNonNull(GraphQLID), | |
| 158 | − }, | |
| 159 | − identifier: { | |
| 160 | − type: new GraphQLNonNull(GraphQLString), | |
| 161 | − }, | |
| 162 | − title: { | |
| 163 | − type: new GraphQLNonNull(GraphQLString), | |
| 164 | − }, | |
| 165 | − team: { | |
| 166 | − type: new GraphQLNonNull(GraphqlTeamType), | |
| 167 | − }, | |
| 168 | − }, | |
| 169 | −}); | |
| 170 | − | |
| 171 | −const GraphqlPageInfoType = new GraphQLObjectType({ | |
| 172 | − name: "PageInfo", | |
| 173 | − fields: { | |
| 174 | − hasNextPage: { | |
| 175 | − type: new GraphQLNonNull(GraphQLBoolean), | |
| 176 | − }, | |
| 177 | − endCursor: { | |
| 178 | − type: GraphQLString, | |
| 179 | − }, | |
| 180 | − }, | |
| 181 | −}); | |
| 182 | − | |
| 183 | −const GraphqlIssueConnectionType = new GraphQLObjectType({ | |
| 184 | − name: "IssueConnection", | |
| 185 | − fields: { | |
| 186 | − nodes: { | |
| 187 | − type: new GraphQLNonNull( | |
| 188 | − new GraphQLList(new GraphQLNonNull(GraphqlIssueType)), | |
| 189 | − ), | |
| 190 | − }, | |
| 191 | − pageInfo: { | |
| 192 | − type: new GraphQLNonNull(GraphqlPageInfoType), | |
| 193 | − }, | |
| 194 | − }, | |
| 195 | −}); | |
| 196 | − | |
| 197 | −const GraphqlCreateIssueInputType = new GraphQLInputObjectType({ | |
| 198 | − name: "CreateIssueInput", | |
| 199 | − fields: { | |
| 200 | − title: { | |
| 201 | − type: new GraphQLNonNull(GraphQLString), | |
| 202 | − }, | |
| 203 | − teamId: { | |
| 204 | − type: GraphQLID, | |
| 205 | − }, | |
| 206 | − }, | |
| 207 | −}); | |
| 208 | − | |
| 209 | −const GraphqlQueryType = new GraphQLObjectType({ | |
| 210 | − name: "Query", | |
| 211 | − fields: { | |
| 212 | − viewer: { | |
| 213 | − type: new GraphQLNonNull(GraphqlUserType), | |
| 214 | − description: "The current user", | |
| 215 | − resolve: () => ({ | |
| 216 | − id: "usr_123", | |
| 217 | − name: "Rhys", | |
| 218 | − }), | |
| 219 | − }, | |
| 220 | − issue: { | |
| 221 | − type: GraphqlIssueType, | |
| 222 | − description: "Find a single issue by id", | |
| 223 | − args: { | |
| 224 | − id: { | |
| 225 | − type: new GraphQLNonNull(GraphQLID), | |
| 226 | − }, | |
| 227 | − }, | |
| 228 | − resolve: (_source, args: { id: string }) => ({ | |
| 229 | − id: args.id, | |
| 230 | − identifier: "ENG-123", | |
| 231 | − title: "Investigate GraphQL support", | |
| 232 | − team: { | |
| 233 | − id: "team_eng", | |
| 234 | − key: "ENG", | |
| 235 | − name: "Engineering", | |
| 236 | − }, | |
| 237 | − }), | |
| 238 | − }, | |
| 239 | − issues: { | |
| 240 | − type: new GraphQLNonNull(GraphqlIssueConnectionType), | |
| 241 | − description: "List issues for the workspace", | |
| 242 | − args: { | |
| 243 | − first: { | |
| 244 | − type: GraphQLInt, | |
| 245 | − }, | |
| 246 | − query: { | |
| 247 | − type: GraphQLString, | |
| 248 | − }, | |
| 249 | − }, | |
| 250 | − resolve: () => ({ | |
| 251 | − nodes: [ | |
| 252 | − { | |
| 253 | − id: "iss_1", | |
| 254 | − identifier: "ENG-123", | |
| 255 | − title: "Investigate GraphQL support", | |
| 256 | − team: { | |
| 257 | − id: "team_eng", | |
| 258 | − key: "ENG", | |
| 259 | − name: "Engineering", | |
| 260 | − }, | |
| 261 | − }, | |
| 262 | − { | |
| 263 | − id: "iss_2", | |
| 264 | − identifier: "ENG-124", | |
| 265 | − title: "Ship GraphQL field tools", | |
| 266 | − team: { | |
| 267 | − id: "team_eng", | |
| 268 | − key: "ENG", | |
| 269 | − name: "Engineering", | |
| 270 | − }, | |
| 271 | − }, | |
| 272 | − ], | |
| 273 | − pageInfo: { | |
| 274 | − hasNextPage: false, | |
| 275 | − endCursor: "iss_2", | |
| 276 | − }, | |
| 277 | − }), | |
| 278 | − }, | |
| 279 | − }, | |
| 280 | −}); | |
| 281 | − | |
| 282 | −const GraphqlMutationType = new GraphQLObjectType({ | |
| 283 | − name: "Mutation", | |
| 284 | − fields: { | |
| 285 | − noop: { | |
| 286 | − type: GraphQLString, | |
| 287 | − resolve: () => "ok", | |
| 288 | − }, | |
| 289 | − createIssue: { | |
| 290 | − type: new GraphQLNonNull(GraphqlIssueType), | |
| 291 | − description: "Create a new issue", | |
| 292 | − args: { | |
| 293 | − input: { | |
| 294 | − type: new GraphQLNonNull(GraphqlCreateIssueInputType), | |
| 295 | − }, | |
| 296 | − }, | |
| 297 | − resolve: ( | |
| 298 | − _source, | |
| 299 | − args: { input: { title: string; teamId?: string | null } }, | |
| 300 | − ) => ({ | |
| 301 | − id: "iss_created", | |
| 302 | − identifier: "ENG-999", | |
| 303 | − title: args.input.title, | |
| 304 | − team: { | |
| 305 | − id: args.input.teamId ?? "team_eng", | |
| 306 | − key: "ENG", | |
| 307 | − name: "Engineering", | |
| 308 | − }, | |
| 309 | − }), | |
| 310 | − }, | |
| 311 | − }, | |
| 312 | −}); | |
| 313 | − | |
| 314 | −const testGraphqlSchema = new GraphQLSchema({ | |
| 315 | − query: GraphqlQueryType, | |
| 316 | − mutation: GraphqlMutationType, | |
| 317 | −}); | |
| 318 | − | |
| 319 | −const closeScope = (scope: Scope.CloseableScope) => | |
| 320 | − Scope.close(scope, Exit.void).pipe(Effect.orDie); | |
| 321 | − | |
| 322 | −const makePersistence: Effect.Effect< | |
| 323 | − SqlControlPlanePersistence, | |
| 324 | − unknown, | |
| 325 | − Scope.Scope | |
| 326 | −> = Effect.acquireRelease( | |
| 327 | − createSqlControlPlanePersistence({ | |
| 328 | − localDataDir: ":memory:", | |
| 329 | − }), | |
| 330 | − (persistence) => | |
| 331 | − Effect.tryPromise({ | |
| 332 | − try: () => persistence.close(), | |
| 333 | − catch: (cause) => | |
| 334 | − cause instanceof Error ? cause : new Error(String(cause)), | |
| 335 | − }).pipe(Effect.orDie), | |
| 336 | −); | |
| 337 | − | |
| 338 | −const makeCountedMcpServer = Effect.acquireRelease( | |
| 339 | − Effect.promise<CountedMcpServer>( | |
| 340 | − () => | |
| 341 | − new Promise<CountedMcpServer>((resolve, reject) => { | |
| 342 | − const counts = { | |
| 343 | − listTools: 0, | |
| 344 | − callTool: 0, | |
| 345 | − }; | |
| 346 | − let closed = false; | |
| 347 | − const app = createMcpExpressApp({ host: "127.0.0.1" }); | |
| 348 | − | |
| 349 | − const createServer = () => { | |
| 350 | − const server = new McpServer( | |
| 351 | − { | |
| 352 | − name: "workspace-execution-environment-test-server", | |
| 353 | − version: "1.0.0", | |
| 354 | − }, | |
| 355 | − { | |
| 356 | − capabilities: { | |
| 357 | − tools: {}, | |
| 358 | − }, | |
| 359 | − }, | |
| 360 | − ); | |
| 361 | − | |
| 362 | − server.registerTool( | |
| 363 | − "echo", | |
| 364 | − { | |
| 365 | − description: "Echo the provided string", | |
| 366 | − inputSchema: { | |
| 367 | − value: z.string(), | |
| 368 | − }, | |
| 369 | − }, | |
| 370 | − async ({ value }: { value: string }) => ({ | |
| 371 | − content: [{ type: "text", text: `echo:${value}` }], | |
| 372 | − }), | |
| 373 | − ); | |
| 374 | − | |
| 375 | − return server; | |
| 376 | − }; | |
| 377 | − | |
| 378 | − const countMethods = (body: unknown) => { | |
| 379 | − const payloads = Array.isArray(body) ? body : [body]; | |
| 380 | − for (const payload of payloads) { | |
| 381 | − if (!payload || typeof payload !== "object") { | |
| 382 | − continue; | |
| 383 | − } | |
| 384 | − | |
| 385 | − const method = "method" in payload ? payload.method : undefined; | |
| 386 | − if (method === "tools/list") { | |
| 387 | − counts.listTools += 1; | |
| 388 | − } else if (method === "tools/call") { | |
| 389 | − counts.callTool += 1; | |
| 390 | − } | |
| 391 | − } | |
| 392 | − }; | |
| 393 | − | |
| 394 | − const handle = async (req: any, res: any, parsedBody?: unknown) => { | |
| 395 | − countMethods(parsedBody); | |
| 396 | − | |
| 397 | − const server = createServer(); | |
| 398 | − const transport = new StreamableHTTPServerTransport({ | |
| 399 | − sessionIdGenerator: undefined, | |
| 400 | − }); | |
| 401 | − | |
| 402 | − try { | |
| 403 | − await server.connect(transport); | |
| 404 | − await transport.handleRequest(req, res, parsedBody); | |
| 405 | − } finally { | |
| 406 | − await transport.close().catch(() => undefined); | |
| 407 | − await server.close().catch(() => undefined); | |
| 408 | − } | |
| 409 | − }; | |
| 410 | − | |
| 411 | − app.post("/mcp", async (req: any, res: any) => { | |
| 412 | − await handle(req, res, req.body); | |
| 413 | − }); | |
| 414 | − | |
| 415 | − app.get("/mcp", async (req: any, res: any) => { | |
| 416 | − await handle(req, res); | |
| 417 | − }); | |
| 418 | − | |
| 419 | − app.delete("/mcp", async (req: any, res: any) => { | |
| 420 | − await handle(req, res, req.body); | |
| 421 | − }); | |
| 422 | − | |
| 423 | − const listener = app.listen(0, "127.0.0.1", () => { | |
| 424 | − const address = listener.address(); | |
| 425 | − if (!address || typeof address === "string") { | |
| 426 | − reject(new Error("failed to resolve MCP test server address")); | |
| 427 | − return; | |
| 428 | − } | |
| 429 | − | |
| 430 | − resolve({ | |
| 431 | − endpoint: `http://127.0.0.1:${address.port}/mcp`, | |
| 432 | − counts, | |
| 433 | − close: async () => { | |
| 434 | − if (closed) { | |
| 435 | − return; | |
| 436 | − } | |
| 437 | − closed = true; | |
| 438 | − | |
| 439 | − await new Promise<void>((closeResolve, closeReject) => { | |
| 440 | − listener.close((error: Error | undefined) => { | |
| 441 | − if (error) { | |
| 442 | − closeReject(error); | |
| 443 | − return; | |
| 444 | − } | |
| 445 | − closeResolve(); | |
| 446 | − }); | |
| 447 | − }); | |
| 448 | − }, | |
| 449 | − }); | |
| 450 | − }); | |
| 451 | − | |
| 452 | − listener.once("error", reject); | |
| 453 | − }), | |
| 454 | − ), | |
| 455 | − (server) => | |
| 456 | − Effect.tryPromise({ | |
| 457 | − try: () => server.close(), | |
| 458 | − catch: (error: unknown) => | |
| 459 | − error instanceof Error ? error : new Error(String(error)), | |
| 460 | − }).pipe(Effect.orDie), | |
| 461 | −); | |
| 462 | − | |
| 463 | −const ownerParam = HttpApiSchema.param("owner", Schema.String); | |
| 464 | −const repoParam = HttpApiSchema.param("repo", Schema.String); | |
| 465 | − | |
| 466 | −class GeneratedReposApi extends HttpApiGroup.make("repos").add( | |
| 467 | − HttpApiEndpoint.get("getRepo")`/repos/${ownerParam}/${repoParam}`.addSuccess( | |
| 468 | − Schema.Struct({ | |
| 469 | − ok: Schema.Boolean, | |
| 470 | − full_name: Schema.String, | |
| 471 | − }), | |
| 472 | − ), | |
| 473 | −) {} | |
| 474 | − | |
| 475 | −class GeneratedApi extends HttpApi.make("generated").add(GeneratedReposApi) {} | |
| 476 | − | |
| 477 | −const makeOpenApiSpecServer = Effect.acquireRelease( | |
| 478 | − Effect.gen(function* () { | |
| 479 | − const seenAuthHeaders: OpenApiSpecServer["seenAuthHeaders"] = []; | |
| 480 | − const handlersLayer = HttpApiBuilder.group( | |
| 481 | − GeneratedApi, | |
| 482 | − "repos", | |
| 483 | − (handlers) => | |
| 484 | − handlers.handle("getRepo", ({ path, request }) => | |
| 485 | − Effect.sync(() => { | |
| 486 | − seenAuthHeaders.push( | |
| 487 | − typeof request.headers.authorization === "string" | |
| 488 | − ? request.headers.authorization | |
1498 more lines aren’t shown here.
This diff is large: its 614 lines aren’t shown here.
This diff is large: its 117 lines aren’t shown here.
This diff is large: its 31 lines aren’t shown here.
This diff is large: its 47 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 160 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 37 lines aren’t shown here.
This diff is large: its 126 lines aren’t shown here.
This diff is large: its 57 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.