History

kill sqlite

Rhys Sullivan committed 8 months ago

Commit a5d22a21 parent 41edbe819 files · +408 −2090Browse files

Unified · Split

19 files
  1. apps/pm/src/approval-runtime.e2e.test.ts +1 −1
  2. apps/pm/src/execute.e2e.test.ts +4 −4
  3. apps/pm/src/main.ts +3 −3
  4. apps/web/README.md +4 −4
  5. apps/web/lib/control-plane/server.ts +4 −4
  6. bun.lock +3 −0
  7. packages/persistence-sql/drizzle.config.pg.ts +0 −14
  8. packages/persistence-sql/drizzle.config.ts +5 −3
  9. packages/persistence-sql/drizzle/0000_colorful_mentallo.sql +0 −0
  10. packages/persistence-sql/drizzle/meta/0000_snapshot.json +1 −1
  11. packages/persistence-sql/drizzle/meta/_journal.json +2 −2
  12. packages/persistence-sql/drizzle/sqlite/0000_curved_raider.sql +0 −224
  13. packages/persistence-sql/drizzle/sqlite/meta/0000_snapshot.json +0 −1437
  14. packages/persistence-sql/drizzle/sqlite/meta/_journal.json +0 −13
  15. packages/persistence-sql/package.json +1 −3
  16. packages/persistence-sql/src/index.ts +7 −7
  17. packages/persistence-sql/src/schema-pg.ts +0 −304
  18. packages/persistence-sql/src/schema.ts +67 −66
  19. packages/persistence-sql/src/sql-internals.ts +306 −0
apps/pm/src/approval-runtime.e2e.test.ts+1−1
@@ -153,7 +153,7 @@ const setupApprovalRuntimeHarness = (runId: string) => · 152 unmodified lines
153 153
154 const persistence = yield* Effect.acquireRelease(154 const persistence = yield* Effect.acquireRelease(
155 makeSqlControlPlanePersistence({155 makeSqlControlPlanePersistence({
156 sqlitePath: path.resolve(stateDir, "control-plane.sqlite"),156 localDataDir: path.resolve(stateDir, "control-plane-pgdata"),
157 }),157 }),
158 (resource) => Effect.promise(() => resource.close()).pipe(Effect.orDie),158 (resource) => Effect.promise(() => resource.close()).pipe(Effect.orDie),
159 );159 );
apps/pm/src/execute.e2e.test.ts+4−4
@@ -95,7 +95,7 @@ const withStateDir = Effect.acquireRelease( · 94 unmodified lines
95 ),95 ),
96);96);
97 97
98const withPmProcess = (port: number, sqlitePath: string) =>98const withPmProcess = (port: number, localDataDir: string) =>
99 Effect.acquireRelease(99 Effect.acquireRelease(
100 Effect.sync(() =>100 Effect.sync(() =>
101 spawn("bun", ["src/main.ts"], {101 spawn("bun", ["src/main.ts"], {
@@ -103,7 +103,7 @@ const withPmProcess = (port: number, sqlitePath: string) => · 1 unmodified line
103 env: {103 env: {
104 ...process.env,104 ...process.env,
105 PORT: String(port),105 PORT: String(port),
106 PM_CONTROL_PLANE_SQLITE_PATH: sqlitePath,106 PM_CONTROL_PLANE_DATA_DIR: localDataDir,
107 },107 },
108 stdio: "pipe",108 stdio: "pipe",
109 }),109 }),
@@ -153,9 +153,9 @@ describe("PM execute E2E", () => { · 43 unmodified lines
153 Effect.gen(function* () {153 Effect.gen(function* () {
154 const port = yield* findFreePort;154 const port = yield* findFreePort;
155 const stateDir = yield* withStateDir;155 const stateDir = yield* withStateDir;
156 const sqlitePath = path.resolve(stateDir, "control-plane.sqlite");156 const localDataDir = path.resolve(stateDir, "control-plane-pgdata");
157 157
158 yield* withPmProcess(port, sqlitePath);158 yield* withPmProcess(port, localDataDir);
159 yield* waitForHealth(port);159 yield* waitForHealth(port);
160 160
161 const { client } = yield* withMcpClient(port);161 const { client } = yield* withMcpClient(port);
apps/pm/src/main.ts+3−3
@@ -179,9 +179,9 @@ const defaultRuntimeKind = · 178 unmodified lines
179const persistence: SqlControlPlanePersistence = await Effect.runPromise(179const persistence: SqlControlPlanePersistence = await Effect.runPromise(
180 makeSqlControlPlanePersistence({180 makeSqlControlPlanePersistence({
181 databaseUrl: process.env.PM_CONTROL_PLANE_DATABASE_URL,181 databaseUrl: process.env.PM_CONTROL_PLANE_DATABASE_URL,
182 sqlitePath:182 localDataDir:
183 process.env.PM_CONTROL_PLANE_SQLITE_PATH183 process.env.PM_CONTROL_PLANE_DATA_DIR
184 ?? path.resolve(pmStateRootDir, "control-plane.sqlite"),184 ?? path.resolve(pmStateRootDir, "control-plane-pgdata"),
185 postgresApplicationName: "executor-v2-pm",185 postgresApplicationName: "executor-v2-pm",
186 }),186 }),
187);187);
apps/web/README.md+4−4
@@ -21,10 +21,10 @@ Database selection priority: · 20 unmodified lines
21- `DATABASE_URL`21- `DATABASE_URL`
22- `POSTGRES_URL`22- `POSTGRES_URL`
23 23
24If none are set, fallback is local SQLite at:24If none are set, fallback is local PGlite data at:
25 25
26- `CONTROL_PLANE_SQLITE_PATH`26- `CONTROL_PLANE_DATA_DIR`
27- default: `.executor-v2/web-state/control-plane.sqlite`27- default: `.executor-v2/web-state/control-plane-pgdata`
28 28
29If needed, `NEXT_PUBLIC_CONTROL_PLANE_BASE_URL` can override the browser base URL.29If needed, `NEXT_PUBLIC_CONTROL_PLANE_BASE_URL` can override the browser base URL.
30 30
@@ -32,7 +32,7 @@ MCP install URL generation: · 1 unmodified line
32 32
33- Derives from existing control-plane/frontend config.33- Derives from existing control-plane/frontend config.
34- Prioritizes server-side `CONTROL_PLANE_UPSTREAM_URL` (or server/base control-plane URL) when available.34- Prioritizes server-side `CONTROL_PLANE_UPSTREAM_URL` (or server/base control-plane URL) when available.
35- In local dev with no DB URL, defaults to local SQLite-backed control-plane in this Next.js app.35- In local dev with no DB URL, defaults to local PGlite-backed control-plane in this Next.js app.
36 36
37WorkOS auth setup (optional but recommended):37WorkOS auth setup (optional but recommended):
38 38
apps/web/lib/control-plane/server.ts+4−4
@@ -40,7 +40,7 @@ const trim = (value: string | undefined): string | undefined => { · 39 unmodified lines
40};40};
41 41
42const defaultControlPlaneStateRootDir = ".executor-v2/web-state";42const defaultControlPlaneStateRootDir = ".executor-v2/web-state";
43const defaultControlPlaneSqliteFile = `${defaultControlPlaneStateRootDir}/control-plane.sqlite`;43const defaultControlPlaneDataDir = `${defaultControlPlaneStateRootDir}/control-plane-pgdata`;
44 44
45type ControlPlaneRuntime = {45type ControlPlaneRuntime = {
46 persistence: SqlControlPlanePersistence;46 persistence: SqlControlPlanePersistence;
@@ -91,8 +91,8 @@ const resolveDatabaseUrl = (): string | undefined => { · 44 unmodified lines
91 return undefined;91 return undefined;
92};92};
93 93
94const resolveSqlitePath = (): string =>94const resolveControlPlaneDataDir = (): string =>
95 trim(process.env.CONTROL_PLANE_SQLITE_PATH) ?? defaultControlPlaneSqliteFile;95 trim(process.env.CONTROL_PLANE_DATA_DIR) ?? defaultControlPlaneDataDir;
96 96
97const resolveStateRootDir = (): string =>97const resolveStateRootDir = (): string =>
98 trim(process.env.CONTROL_PLANE_STATE_ROOT_DIR) ?? defaultControlPlaneStateRootDir;98 trim(process.env.CONTROL_PLANE_STATE_ROOT_DIR) ?? defaultControlPlaneStateRootDir;
@@ -184,7 +184,7 @@ const createControlPlaneRuntime = async (): Promise<ControlPlaneRuntime> => { · 85 unmodified lines
184 const persistence = await Effect.runPromise(184 const persistence = await Effect.runPromise(
185 makeSqlControlPlanePersistence({185 makeSqlControlPlanePersistence({
186 databaseUrl: resolveDatabaseUrl(),186 databaseUrl: resolveDatabaseUrl(),
187 sqlitePath: resolveSqlitePath(),187 localDataDir: resolveControlPlaneDataDir(),
188 postgresApplicationName: "executor-v2-web",188 postgresApplicationName: "executor-v2-web",
189 }),189 }),
190 );190 );
bun.lock+3−0
@@ -163,6 +163,7 @@ · 162 unmodified lines
163 "packages/persistence-sql": {163 "packages/persistence-sql": {
164 "name": "@executor-v2/persistence-sql",164 "name": "@executor-v2/persistence-sql",
165 "dependencies": {165 "dependencies": {
166 "@electric-sql/pglite": "^0.3.10",
166 "@executor-v2/persistence-ports": "workspace:*",167 "@executor-v2/persistence-ports": "workspace:*",
167 "@executor-v2/schema": "workspace:*",168 "@executor-v2/schema": "workspace:*",
168 "drizzle-orm": "^0.44.7",169 "drizzle-orm": "^0.44.7",
@@ -359,6 +360,8 @@ · 190 unmodified lines
359 360
360 "@effect/workflow": ["@effect/workflow@0.15.2", "", { "peerDependencies": { "@effect/experimental": "^0.57.11", "@effect/platform": "^0.93.6", "@effect/rpc": "^0.72.2", "effect": "^3.19.10" } }, "sha512-UAo5QWEvyyKsnf4EQ7WL3zwiuZS4Wd5fmAxdpcpZSIxNOvsABp3DOuyRCiidD8l3sQhdPwES/UsVK4QOCQ7wew=="],361 "@effect/workflow": ["@effect/workflow@0.15.2", "", { "peerDependencies": { "@effect/experimental": "^0.57.11", "@effect/platform": "^0.93.6", "@effect/rpc": "^0.72.2", "effect": "^3.19.10" } }, "sha512-UAo5QWEvyyKsnf4EQ7WL3zwiuZS4Wd5fmAxdpcpZSIxNOvsABp3DOuyRCiidD8l3sQhdPwES/UsVK4QOCQ7wew=="],
361 362
363 "@electric-sql/pglite": ["@electric-sql/pglite@0.3.15", "", {}, "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ=="],
364
362 "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],365 "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="],
363 366
364 "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="],367 "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="],
packages/persistence-sql/drizzle.config.pg.ts+0−14
@@ -1,14 +0,0 @@
1import { defineConfig } from "drizzle-kit";
2
3const databaseUrl = process.env.DATABASE_URL ?? "postgres://localhost:5432/executor_v2";
4
5export default defineConfig({
6 dialect: "postgresql",
7 schema: "./src/schema-pg.ts",
8 out: "./drizzle/postgres",
9 dbCredentials: {
10 url: databaseUrl,
11 },
12 strict: true,
13 verbose: true,
14});
packages/persistence-sql/drizzle.config.ts+5−3
@@ -1,11 +1,13 @@
1import { defineConfig } from "drizzle-kit";1import { defineConfig } from "drizzle-kit";
2 2
3const databaseUrl = process.env.DATABASE_URL ?? "postgres://localhost:5432/executor_v2";
4
3export default defineConfig({5export default defineConfig({
4 dialect: "sqlite",6 dialect: "postgresql",
5 schema: "./src/schema.ts",7 schema: "./src/schema.ts",
6 out: "./drizzle/sqlite",8 out: "./drizzle",
7 dbCredentials: {9 dbCredentials: {
8 url: "./.executor-v2/control-plane.sqlite",10 url: databaseUrl,
9 },11 },
10 strict: true,12 strict: true,
11 verbose: true,13 verbose: true,
packages/persistence-sql/drizzle/0000_colorful_mentallo.sqlrenamed from packages/persistence-sql/drizzle/postgres/0000_spicy_barracuda.sql+0−0

File renamed without changes.

packages/persistence-sql/drizzle/meta/0000_snapshot.jsonrenamed from packages/persistence-sql/drizzle/postgres/meta/0000_snapshot.json+1−1
@@ -1,5 +1,5 @@
1{1{
2 "id": "5ce8d2a0-b31c-4d34-bd01-c638ca0d5d87",2 "id": "ab2ffa01-b774-4a84-bb66-50979a649de7",
3 "prevId": "00000000-0000-0000-0000-000000000000",3 "prevId": "00000000-0000-0000-0000-000000000000",
4 "version": "7",4 "version": "7",
5 "dialect": "postgresql",5 "dialect": "postgresql",
packages/persistence-sql/drizzle/meta/_journal.jsonrenamed from packages/persistence-sql/drizzle/postgres/meta/_journal.json+2−2
@@ -5,8 +5,8 @@ · 4 unmodified lines
5 {5 {
6 "idx": 0,6 "idx": 0,
7 "version": "7",7 "version": "7",
8 "when": 1772506152805,8 "when": 1772507285421,
9 "tag": "0000_spicy_barracuda",9 "tag": "0000_colorful_mentallo",
10 "breakpoints": true10 "breakpoints": true
11 }11 }
12 ]12 ]
packages/persistence-sql/drizzle/sqlite/0000_curved_raider.sql+0−224
@@ -1,224 +0,0 @@
1CREATE TABLE `approvals` (
2 `id` text PRIMARY KEY NOT NULL,
3 `workspace_id` text NOT NULL,
4 `task_run_id` text NOT NULL,
5 `call_id` text NOT NULL,
6 `tool_path` text NOT NULL,
7 `status` text NOT NULL,
8 `input_preview_json` text NOT NULL,
9 `reason` text,
10 `requested_at` integer NOT NULL,
11 `resolved_at` integer
12);
13--> statement-breakpoint
14CREATE INDEX `approvals_workspace_idx` ON `approvals` (`workspace_id`);--> statement-breakpoint
15CREATE INDEX `approvals_task_run_idx` ON `approvals` (`task_run_id`);--> statement-breakpoint
16CREATE TABLE `auth_connections` (
17 `id` text PRIMARY KEY NOT NULL,
18 `organization_id` text NOT NULL,
19 `workspace_id` text,
20 `account_id` text,
21 `owner_type` text NOT NULL,
22 `strategy` text NOT NULL,
23 `display_name` text NOT NULL,
24 `status` text NOT NULL,
25 `status_reason` text,
26 `last_auth_error_class` text,
27 `metadata_json` text,
28 `additional_headers_json` text,
29 `created_by_account_id` text,
30 `created_at` integer NOT NULL,
31 `updated_at` integer NOT NULL,
32 `last_used_at` integer
33);
34--> statement-breakpoint
35CREATE INDEX `auth_connections_org_idx` ON `auth_connections` (`organization_id`);--> statement-breakpoint
36CREATE INDEX `auth_connections_workspace_idx` ON `auth_connections` (`workspace_id`);--> statement-breakpoint
37CREATE INDEX `auth_connections_account_idx` ON `auth_connections` (`account_id`);--> statement-breakpoint
38CREATE TABLE `auth_materials` (
39 `id` text PRIMARY KEY NOT NULL,
40 `connection_id` text NOT NULL,
41 `ciphertext` text NOT NULL,
42 `key_version` text NOT NULL,
43 `created_at` integer NOT NULL,
44 `updated_at` integer NOT NULL
45);
46--> statement-breakpoint
47CREATE INDEX `auth_materials_connection_idx` ON `auth_materials` (`connection_id`);--> statement-breakpoint
48CREATE TABLE `oauth_states` (
49 `id` text PRIMARY KEY NOT NULL,
50 `connection_id` text NOT NULL,
51 `access_token_ciphertext` text NOT NULL,
52 `refresh_token_ciphertext` text,
53 `key_version` text NOT NULL,
54 `expires_at` integer,
55 `scope` text,
56 `token_type` text,
57 `issuer` text,
58 `refresh_config_json` text,
59 `token_version` integer NOT NULL,
60 `lease_holder` text,
61 `lease_expires_at` integer,
62 `lease_fence` integer NOT NULL,
63 `last_refresh_at` integer,
64 `last_refresh_error_class` text,
65 `last_refresh_error` text,
66 `reauth_required_at` integer,
67 `created_at` integer NOT NULL,
68 `updated_at` integer NOT NULL
69);
70--> statement-breakpoint
71CREATE INDEX `oauth_states_connection_idx` ON `oauth_states` (`connection_id`);--> statement-breakpoint
72CREATE TABLE `organization_memberships` (
73 `id` text PRIMARY KEY NOT NULL,
74 `organization_id` text NOT NULL,
75 `account_id` text NOT NULL,
76 `role` text NOT NULL,
77 `status` text NOT NULL,
78 `billable` integer NOT NULL,
79 `invited_by_account_id` text,
80 `joined_at` integer,
81 `created_at` integer NOT NULL,
82 `updated_at` integer NOT NULL
83);
84--> statement-breakpoint
85CREATE INDEX `organization_memberships_org_idx` ON `organization_memberships` (`organization_id`);--> statement-breakpoint
86CREATE INDEX `organization_memberships_account_idx` ON `organization_memberships` (`account_id`);--> statement-breakpoint
87CREATE TABLE `organizations` (
88 `id` text PRIMARY KEY NOT NULL,
89 `slug` text NOT NULL,
90 `name` text NOT NULL,
91 `status` text NOT NULL,
92 `created_by_account_id` text,
93 `created_at` integer NOT NULL,
94 `updated_at` integer NOT NULL
95);
96--> statement-breakpoint
97CREATE TABLE `policies` (
98 `id` text PRIMARY KEY NOT NULL,
99 `workspace_id` text NOT NULL,
100 `tool_path_pattern` text NOT NULL,
101 `decision` text NOT NULL,
102 `created_at` integer NOT NULL,
103 `updated_at` integer NOT NULL
104);
105--> statement-breakpoint
106CREATE INDEX `policies_workspace_idx` ON `policies` (`workspace_id`);--> statement-breakpoint
107CREATE TABLE `profile` (
108 `id` text PRIMARY KEY NOT NULL,
109 `default_workspace_id` text,
110 `display_name` text NOT NULL,
111 `runtime_mode` text NOT NULL,
112 `created_at` integer NOT NULL,
113 `updated_at` integer NOT NULL
114);
115--> statement-breakpoint
116CREATE TABLE `source_auth_bindings` (
117 `id` text PRIMARY KEY NOT NULL,
118 `source_id` text NOT NULL,
119 `connection_id` text NOT NULL,
120 `organization_id` text NOT NULL,
121 `workspace_id` text,
122 `account_id` text,
123 `scope_type` text NOT NULL,
124 `selector` text,
125 `enabled` integer NOT NULL,
126 `created_at` integer NOT NULL,
127 `updated_at` integer NOT NULL
128);
129--> statement-breakpoint
130CREATE INDEX `source_auth_bindings_source_idx` ON `source_auth_bindings` (`source_id`);--> statement-breakpoint
131CREATE INDEX `source_auth_bindings_connection_idx` ON `source_auth_bindings` (`connection_id`);--> statement-breakpoint
132CREATE INDEX `source_auth_bindings_org_idx` ON `source_auth_bindings` (`organization_id`);--> statement-breakpoint
133CREATE INDEX `source_auth_bindings_workspace_idx` ON `source_auth_bindings` (`workspace_id`);--> statement-breakpoint
134CREATE INDEX `source_auth_bindings_account_idx` ON `source_auth_bindings` (`account_id`);--> statement-breakpoint
135CREATE TABLE `sources` (
136 `workspace_id` text NOT NULL,
137 `source_id` text NOT NULL,
138 `name` text NOT NULL,
139 `kind` text NOT NULL,
140 `endpoint` text NOT NULL,
141 `status` text NOT NULL,
142 `enabled` integer NOT NULL,
143 `config_json` text NOT NULL,
144 `source_hash` text,
145 `last_error` text,
146 `created_at` integer NOT NULL,
147 `updated_at` integer NOT NULL,
148 PRIMARY KEY(`workspace_id`, `source_id`)
149);
150--> statement-breakpoint
151CREATE INDEX `sources_workspace_name_idx` ON `sources` (`workspace_id`,`name`);--> statement-breakpoint
152CREATE TABLE `storage_instances` (
153 `id` text PRIMARY KEY NOT NULL,
154 `scope_type` text NOT NULL,
155 `durability` text NOT NULL,
156 `status` text NOT NULL,
157 `provider` text NOT NULL,
158 `backend_key` text NOT NULL,
159 `organization_id` text NOT NULL,
160 `workspace_id` text,
161 `account_id` text,
162 `created_by_account_id` text,
163 `purpose` text,
164 `size_bytes` integer,
165 `file_count` integer,
166 `created_at` integer NOT NULL,
167 `updated_at` integer NOT NULL,
168 `last_seen_at` integer NOT NULL,
169 `closed_at` integer,
170 `expires_at` integer
171);
172--> statement-breakpoint
173CREATE INDEX `storage_instances_org_idx` ON `storage_instances` (`organization_id`);--> statement-breakpoint
174CREATE INDEX `storage_instances_workspace_idx` ON `storage_instances` (`workspace_id`);--> statement-breakpoint
175CREATE TABLE `sync_states` (
176 `id` text PRIMARY KEY NOT NULL,
177 `workspace_id` text NOT NULL,
178 `target` text NOT NULL,
179 `target_url` text NOT NULL,
180 `linked_subject` text,
181 `cursor` text,
182 `last_sync_at` integer,
183 `updated_at` integer NOT NULL
184);
185--> statement-breakpoint
186CREATE INDEX `sync_states_workspace_idx` ON `sync_states` (`workspace_id`);--> statement-breakpoint
187CREATE TABLE `task_runs` (
188 `id` text PRIMARY KEY NOT NULL,
189 `workspace_id` text NOT NULL,
190 `account_id` text NOT NULL,
191 `session_id` text NOT NULL,
192 `runtime_id` text NOT NULL,
193 `code_hash` text NOT NULL,
194 `status` text NOT NULL,
195 `started_at` integer,
196 `completed_at` integer,
197 `exit_code` integer,
198 `error` text
199);
200--> statement-breakpoint
201CREATE INDEX `task_runs_workspace_idx` ON `task_runs` (`workspace_id`);--> statement-breakpoint
202CREATE TABLE `tool_artifacts` (
203 `id` text NOT NULL,
204 `workspace_id` text NOT NULL,
205 `source_id` text NOT NULL,
206 `source_hash` text NOT NULL,
207 `tool_count` integer NOT NULL,
208 `manifest_json` text NOT NULL,
209 `created_at` integer NOT NULL,
210 `updated_at` integer NOT NULL,
211 PRIMARY KEY(`workspace_id`, `source_id`)
212);
213--> statement-breakpoint
214CREATE UNIQUE INDEX `tool_artifacts_id_idx` ON `tool_artifacts` (`id`);--> statement-breakpoint
215CREATE TABLE `workspaces` (
216 `id` text PRIMARY KEY NOT NULL,
217 `organization_id` text NOT NULL,
218 `name` text NOT NULL,
219 `created_by_account_id` text,
220 `created_at` integer NOT NULL,
221 `updated_at` integer NOT NULL
222);
223--> statement-breakpoint
224CREATE INDEX `workspaces_org_idx` ON `workspaces` (`organization_id`);
packages/persistence-sql/drizzle/sqlite/meta/0000_snapshot.json+0−1437
@@ -1,1437 +0,0 @@
1{
2 "version": "6",
3 "dialect": "sqlite",
4 "id": "012495d1-d78e-4f31-a64f-7328fdf970c0",
5 "prevId": "00000000-0000-0000-0000-000000000000",
6 "tables": {
7 "approvals": {
8 "name": "approvals",
9 "columns": {
10 "id": {
11 "name": "id",
12 "type": "text",
13 "primaryKey": true,
14 "notNull": true,
15 "autoincrement": false
16 },
17 "workspace_id": {
18 "name": "workspace_id",
19 "type": "text",
20 "primaryKey": false,
21 "notNull": true,
22 "autoincrement": false
23 },
24 "task_run_id": {
25 "name": "task_run_id",
26 "type": "text",
27 "primaryKey": false,
28 "notNull": true,
29 "autoincrement": false
30 },
31 "call_id": {
32 "name": "call_id",
33 "type": "text",
34 "primaryKey": false,
35 "notNull": true,
36 "autoincrement": false
37 },
38 "tool_path": {
39 "name": "tool_path",
40 "type": "text",
41 "primaryKey": false,
42 "notNull": true,
43 "autoincrement": false
44 },
45 "status": {
46 "name": "status",
47 "type": "text",
48 "primaryKey": false,
49 "notNull": true,
50 "autoincrement": false
51 },
52 "input_preview_json": {
53 "name": "input_preview_json",
54 "type": "text",
55 "primaryKey": false,
56 "notNull": true,
57 "autoincrement": false
58 },
59 "reason": {
60 "name": "reason",
61 "type": "text",
62 "primaryKey": false,
63 "notNull": false,
64 "autoincrement": false
65 },
66 "requested_at": {
67 "name": "requested_at",
68 "type": "integer",
69 "primaryKey": false,
70 "notNull": true,
71 "autoincrement": false
72 },
73 "resolved_at": {
74 "name": "resolved_at",
75 "type": "integer",
76 "primaryKey": false,
77 "notNull": false,
78 "autoincrement": false
79 }
80 },
81 "indexes": {
82 "approvals_workspace_idx": {
83 "name": "approvals_workspace_idx",
84 "columns": [
85 "workspace_id"
86 ],
87 "isUnique": false
88 },
89 "approvals_task_run_idx": {
90 "name": "approvals_task_run_idx",
91 "columns": [
92 "task_run_id"
93 ],
94 "isUnique": false
95 }
96 },
97 "foreignKeys": {},
98 "compositePrimaryKeys": {},
99 "uniqueConstraints": {},
100 "checkConstraints": {}
101 },
102 "auth_connections": {
103 "name": "auth_connections",
104 "columns": {
105 "id": {
106 "name": "id",
107 "type": "text",
108 "primaryKey": true,
109 "notNull": true,
110 "autoincrement": false
111 },
112 "organization_id": {
113 "name": "organization_id",
114 "type": "text",
115 "primaryKey": false,
116 "notNull": true,
117 "autoincrement": false
118 },
119 "workspace_id": {
120 "name": "workspace_id",
121 "type": "text",
122 "primaryKey": false,
123 "notNull": false,
124 "autoincrement": false
125 },
126 "account_id": {
127 "name": "account_id",
128 "type": "text",
129 "primaryKey": false,
130 "notNull": false,
131 "autoincrement": false
132 },
133 "owner_type": {
134 "name": "owner_type",
135 "type": "text",
136 "primaryKey": false,
137 "notNull": true,
138 "autoincrement": false
139 },
140 "strategy": {
141 "name": "strategy",
142 "type": "text",
143 "primaryKey": false,
144 "notNull": true,
145 "autoincrement": false
146 },
147 "display_name": {
148 "name": "display_name",
149 "type": "text",
150 "primaryKey": false,
151 "notNull": true,
152 "autoincrement": false
153 },
154 "status": {
155 "name": "status",
156 "type": "text",
157 "primaryKey": false,
158 "notNull": true,
159 "autoincrement": false
160 },
161 "status_reason": {
162 "name": "status_reason",
163 "type": "text",
164 "primaryKey": false,
165 "notNull": false,
166 "autoincrement": false
167 },
168 "last_auth_error_class": {
169 "name": "last_auth_error_class",
170 "type": "text",
171 "primaryKey": false,
172 "notNull": false,
173 "autoincrement": false
174 },
175 "metadata_json": {
176 "name": "metadata_json",
177 "type": "text",
178 "primaryKey": false,
179 "notNull": false,
180 "autoincrement": false
181 },
182 "additional_headers_json": {
183 "name": "additional_headers_json",
184 "type": "text",
185 "primaryKey": false,
186 "notNull": false,
187 "autoincrement": false
188 },
189 "created_by_account_id": {
190 "name": "created_by_account_id",
191 "type": "text",
192 "primaryKey": false,
193 "notNull": false,
194 "autoincrement": false
195 },
196 "created_at": {
197 "name": "created_at",
198 "type": "integer",
199 "primaryKey": false,
200 "notNull": true,
201 "autoincrement": false
202 },
203 "updated_at": {
204 "name": "updated_at",
205 "type": "integer",
206 "primaryKey": false,
207 "notNull": true,
208 "autoincrement": false
209 },
210 "last_used_at": {
211 "name": "last_used_at",
212 "type": "integer",
213 "primaryKey": false,
214 "notNull": false,
215 "autoincrement": false
216 }
217 },
218 "indexes": {
219 "auth_connections_org_idx": {
220 "name": "auth_connections_org_idx",
221 "columns": [
222 "organization_id"
223 ],
224 "isUnique": false
225 },
226 "auth_connections_workspace_idx": {
227 "name": "auth_connections_workspace_idx",
228 "columns": [
229 "workspace_id"
230 ],
231 "isUnique": false
232 },
233 "auth_connections_account_idx": {
234 "name": "auth_connections_account_idx",
235 "columns": [
236 "account_id"
237 ],
238 "isUnique": false
239 }
240 },
241 "foreignKeys": {},
242 "compositePrimaryKeys": {},
243 "uniqueConstraints": {},
244 "checkConstraints": {}
245 },
246 "auth_materials": {
247 "name": "auth_materials",
248 "columns": {
249 "id": {
250 "name": "id",
251 "type": "text",
252 "primaryKey": true,
253 "notNull": true,
254 "autoincrement": false
255 },
256 "connection_id": {
257 "name": "connection_id",
258 "type": "text",
259 "primaryKey": false,
260 "notNull": true,
261 "autoincrement": false
262 },
263 "ciphertext": {
264 "name": "ciphertext",
265 "type": "text",
266 "primaryKey": false,
267 "notNull": true,
268 "autoincrement": false
269 },
270 "key_version": {
271 "name": "key_version",
272 "type": "text",
273 "primaryKey": false,
274 "notNull": true,
275 "autoincrement": false
276 },
277 "created_at": {
278 "name": "created_at",
279 "type": "integer",
280 "primaryKey": false,
281 "notNull": true,
282 "autoincrement": false
283 },
284 "updated_at": {
285 "name": "updated_at",
286 "type": "integer",
287 "primaryKey": false,
288 "notNull": true,
289 "autoincrement": false
290 }
291 },
292 "indexes": {
293 "auth_materials_connection_idx": {
294 "name": "auth_materials_connection_idx",
295 "columns": [
296 "connection_id"
297 ],
298 "isUnique": false
299 }
300 },
301 "foreignKeys": {},
302 "compositePrimaryKeys": {},
303 "uniqueConstraints": {},
304 "checkConstraints": {}
305 },
306 "oauth_states": {
307 "name": "oauth_states",
308 "columns": {
309 "id": {
310 "name": "id",
311 "type": "text",
312 "primaryKey": true,
313 "notNull": true,
314 "autoincrement": false
315 },
316 "connection_id": {
317 "name": "connection_id",
318 "type": "text",
319 "primaryKey": false,
320 "notNull": true,
321 "autoincrement": false
322 },
323 "access_token_ciphertext": {
324 "name": "access_token_ciphertext",
325 "type": "text",
326 "primaryKey": false,
327 "notNull": true,
328 "autoincrement": false
329 },
330 "refresh_token_ciphertext": {
331 "name": "refresh_token_ciphertext",
332 "type": "text",
333 "primaryKey": false,
334 "notNull": false,
335 "autoincrement": false
336 },
337 "key_version": {
338 "name": "key_version",
339 "type": "text",
340 "primaryKey": false,
341 "notNull": true,
342 "autoincrement": false
343 },
344 "expires_at": {
345 "name": "expires_at",
346 "type": "integer",
347 "primaryKey": false,
348 "notNull": false,
349 "autoincrement": false
350 },
351 "scope": {
352 "name": "scope",
353 "type": "text",
354 "primaryKey": false,
355 "notNull": false,
356 "autoincrement": false
357 },
358 "token_type": {
359 "name": "token_type",
360 "type": "text",
361 "primaryKey": false,
362 "notNull": false,
363 "autoincrement": false
364 },
365 "issuer": {
366 "name": "issuer",
367 "type": "text",
368 "primaryKey": false,
369 "notNull": false,
370 "autoincrement": false
371 },
372 "refresh_config_json": {
373 "name": "refresh_config_json",
374 "type": "text",
375 "primaryKey": false,
376 "notNull": false,
377 "autoincrement": false
378 },
379 "token_version": {
380 "name": "token_version",
381 "type": "integer",
382 "primaryKey": false,
383 "notNull": true,
384 "autoincrement": false
385 },
386 "lease_holder": {
387 "name": "lease_holder",
388 "type": "text",
389 "primaryKey": false,
390 "notNull": false,
391 "autoincrement": false
392 },
393 "lease_expires_at": {
394 "name": "lease_expires_at",
395 "type": "integer",
396 "primaryKey": false,
397 "notNull": false,
398 "autoincrement": false
399 },
400 "lease_fence": {
401 "name": "lease_fence",
402 "type": "integer",
403 "primaryKey": false,
404 "notNull": true,
405 "autoincrement": false
406 },
407 "last_refresh_at": {
408 "name": "last_refresh_at",
409 "type": "integer",
410 "primaryKey": false,
411 "notNull": false,
412 "autoincrement": false
413 },
414 "last_refresh_error_class": {
415 "name": "last_refresh_error_class",
416 "type": "text",
417 "primaryKey": false,
418 "notNull": false,
419 "autoincrement": false
420 },
421 "last_refresh_error": {
422 "name": "last_refresh_error",
423 "type": "text",
424 "primaryKey": false,
425 "notNull": false,
426 "autoincrement": false
427 },
428 "reauth_required_at": {
429 "name": "reauth_required_at",
430 "type": "integer",
431 "primaryKey": false,
432 "notNull": false,
433 "autoincrement": false
434 },
435 "created_at": {
436 "name": "created_at",
437 "type": "integer",
438 "primaryKey": false,
439 "notNull": true,
440 "autoincrement": false
441 },
442 "updated_at": {
443 "name": "updated_at",
444 "type": "integer",
445 "primaryKey": false,
446 "notNull": true,
447 "autoincrement": false
448 }
449 },
450 "indexes": {
451 "oauth_states_connection_idx": {
452 "name": "oauth_states_connection_idx",
453 "columns": [
454 "connection_id"
455 ],
456 "isUnique": false
457 }
458 },
459 "foreignKeys": {},
460 "compositePrimaryKeys": {},
461 "uniqueConstraints": {},
462 "checkConstraints": {}
463 },
464 "organization_memberships": {
465 "name": "organization_memberships",
466 "columns": {
467 "id": {
468 "name": "id",
469 "type": "text",
470 "primaryKey": true,
471 "notNull": true,
472 "autoincrement": false
473 },
474 "organization_id": {
475 "name": "organization_id",
476 "type": "text",
477 "primaryKey": false,
478 "notNull": true,
479 "autoincrement": false
480 },
481 "account_id": {
482 "name": "account_id",
483 "type": "text",
484 "primaryKey": false,
485 "notNull": true,
486 "autoincrement": false
487 },
488 "role": {
489 "name": "role",
490 "type": "text",
491 "primaryKey": false,
492 "notNull": true,
493 "autoincrement": false
494 },
495 "status": {
496 "name": "status",
497 "type": "text",
498 "primaryKey": false,
499 "notNull": true,
500 "autoincrement": false
501 },
502 "billable": {
503 "name": "billable",
504 "type": "integer",
505 "primaryKey": false,
506 "notNull": true,
507 "autoincrement": false
508 },
509 "invited_by_account_id": {
510 "name": "invited_by_account_id",
511 "type": "text",
512 "primaryKey": false,
513 "notNull": false,
514 "autoincrement": false
515 },
516 "joined_at": {
517 "name": "joined_at",
518 "type": "integer",
519 "primaryKey": false,
520 "notNull": false,
521 "autoincrement": false
522 },
523 "created_at": {
524 "name": "created_at",
525 "type": "integer",
526 "primaryKey": false,
527 "notNull": true,
528 "autoincrement": false
529 },
530 "updated_at": {
531 "name": "updated_at",
532 "type": "integer",
533 "primaryKey": false,
534 "notNull": true,
535 "autoincrement": false
536 }
537 },
538 "indexes": {
539 "organization_memberships_org_idx": {
540 "name": "organization_memberships_org_idx",
541 "columns": [
542 "organization_id"
543 ],
544 "isUnique": false
545 },
546 "organization_memberships_account_idx": {
547 "name": "organization_memberships_account_idx",
548 "columns": [
549 "account_id"
550 ],
551 "isUnique": false
552 }
553 },
554 "foreignKeys": {},
555 "compositePrimaryKeys": {},
556 "uniqueConstraints": {},
557 "checkConstraints": {}
558 },
559 "organizations": {
560 "name": "organizations",
561 "columns": {
562 "id": {
563 "name": "id",
564 "type": "text",
565 "primaryKey": true,
566 "notNull": true,
567 "autoincrement": false
568 },
569 "slug": {
570 "name": "slug",
571 "type": "text",
572 "primaryKey": false,
573 "notNull": true,
574 "autoincrement": false
575 },
576 "name": {
577 "name": "name",
578 "type": "text",
579 "primaryKey": false,
580 "notNull": true,
581 "autoincrement": false
582 },
583 "status": {
584 "name": "status",
585 "type": "text",
586 "primaryKey": false,
587 "notNull": true,
588 "autoincrement": false
589 },
590 "created_by_account_id": {
591 "name": "created_by_account_id",
592 "type": "text",
593 "primaryKey": false,
594 "notNull": false,
595 "autoincrement": false
596 },
597 "created_at": {
598 "name": "created_at",
599 "type": "integer",
600 "primaryKey": false,
601 "notNull": true,
602 "autoincrement": false
603 },
604 "updated_at": {
605 "name": "updated_at",
606 "type": "integer",
607 "primaryKey": false,
608 "notNull": true,
609 "autoincrement": false
610 }
611 },
612 "indexes": {},
613 "foreignKeys": {},
614 "compositePrimaryKeys": {},
615 "uniqueConstraints": {},
616 "checkConstraints": {}
617 },
618 "policies": {
619 "name": "policies",
620 "columns": {
621 "id": {
622 "name": "id",
623 "type": "text",
624 "primaryKey": true,
625 "notNull": true,
626 "autoincrement": false
627 },
628 "workspace_id": {
629 "name": "workspace_id",
630 "type": "text",
631 "primaryKey": false,
632 "notNull": true,
633 "autoincrement": false
634 },
635 "tool_path_pattern": {
636 "name": "tool_path_pattern",
637 "type": "text",
638 "primaryKey": false,
639 "notNull": true,
640 "autoincrement": false
641 },
642 "decision": {
643 "name": "decision",
644 "type": "text",
645 "primaryKey": false,
646 "notNull": true,
647 "autoincrement": false
648 },
649 "created_at": {
650 "name": "created_at",
651 "type": "integer",
652 "primaryKey": false,
653 "notNull": true,
654 "autoincrement": false
655 },
656 "updated_at": {
657 "name": "updated_at",
658 "type": "integer",
659 "primaryKey": false,
660 "notNull": true,
661 "autoincrement": false
662 }
663 },
664 "indexes": {
665 "policies_workspace_idx": {
666 "name": "policies_workspace_idx",
667 "columns": [
668 "workspace_id"
669 ],
670 "isUnique": false
671 }
672 },
673 "foreignKeys": {},
674 "compositePrimaryKeys": {},
675 "uniqueConstraints": {},
676 "checkConstraints": {}
677 },
678 "profile": {
679 "name": "profile",
680 "columns": {
681 "id": {
682 "name": "id",
683 "type": "text",
684 "primaryKey": true,
685 "notNull": true,
686 "autoincrement": false
687 },
688 "default_workspace_id": {
689 "name": "default_workspace_id",
690 "type": "text",
691 "primaryKey": false,
692 "notNull": false,
693 "autoincrement": false
694 },
695 "display_name": {
696 "name": "display_name",
697 "type": "text",
698 "primaryKey": false,
699 "notNull": true,
700 "autoincrement": false
701 },
702 "runtime_mode": {
703 "name": "runtime_mode",
704 "type": "text",
705 "primaryKey": false,
706 "notNull": true,
707 "autoincrement": false
708 },
709 "created_at": {
710 "name": "created_at",
711 "type": "integer",
712 "primaryKey": false,
713 "notNull": true,
714 "autoincrement": false
715 },
716 "updated_at": {
717 "name": "updated_at",
718 "type": "integer",
719 "primaryKey": false,
720 "notNull": true,
721 "autoincrement": false
722 }
723 },
724 "indexes": {},
725 "foreignKeys": {},
726 "compositePrimaryKeys": {},
727 "uniqueConstraints": {},
728 "checkConstraints": {}
729 },
730 "source_auth_bindings": {
731 "name": "source_auth_bindings",
732 "columns": {
733 "id": {
734 "name": "id",
735 "type": "text",
736 "primaryKey": true,
737 "notNull": true,
738 "autoincrement": false
739 },
740 "source_id": {
741 "name": "source_id",
742 "type": "text",
743 "primaryKey": false,
744 "notNull": true,
745 "autoincrement": false
746 },
747 "connection_id": {
748 "name": "connection_id",
749 "type": "text",
750 "primaryKey": false,
751 "notNull": true,
752 "autoincrement": false
753 },
754 "organization_id": {
755 "name": "organization_id",
756 "type": "text",
757 "primaryKey": false,
758 "notNull": true,
759 "autoincrement": false
760 },
761 "workspace_id": {
762 "name": "workspace_id",
763 "type": "text",
764 "primaryKey": false,
765 "notNull": false,
766 "autoincrement": false
767 },
768 "account_id": {
769 "name": "account_id",
770 "type": "text",
771 "primaryKey": false,
772 "notNull": false,
773 "autoincrement": false
774 },
775 "scope_type": {
776 "name": "scope_type",
777 "type": "text",
778 "primaryKey": false,
779 "notNull": true,
780 "autoincrement": false
781 },
782 "selector": {
783 "name": "selector",
784 "type": "text",
785 "primaryKey": false,
786 "notNull": false,
787 "autoincrement": false
788 },
789 "enabled": {
790 "name": "enabled",
791 "type": "integer",
792 "primaryKey": false,
793 "notNull": true,
794 "autoincrement": false
795 },
796 "created_at": {
797 "name": "created_at",
798 "type": "integer",
799 "primaryKey": false,
800 "notNull": true,
801 "autoincrement": false
802 },
803 "updated_at": {
804 "name": "updated_at",
805 "type": "integer",
806 "primaryKey": false,
807 "notNull": true,
808 "autoincrement": false
809 }
810 },
811 "indexes": {
812 "source_auth_bindings_source_idx": {
813 "name": "source_auth_bindings_source_idx",
814 "columns": [
815 "source_id"
816 ],
817 "isUnique": false
818 },
819 "source_auth_bindings_connection_idx": {
820 "name": "source_auth_bindings_connection_idx",
821 "columns": [
822 "connection_id"
823 ],
824 "isUnique": false
825 },
826 "source_auth_bindings_org_idx": {
827 "name": "source_auth_bindings_org_idx",
828 "columns": [
829 "organization_id"
830 ],
831 "isUnique": false
832 },
833 "source_auth_bindings_workspace_idx": {
834 "name": "source_auth_bindings_workspace_idx",
835 "columns": [
836 "workspace_id"
837 ],
838 "isUnique": false
839 },
840 "source_auth_bindings_account_idx": {
841 "name": "source_auth_bindings_account_idx",
842 "columns": [
843 "account_id"
844 ],
845 "isUnique": false
846 }
847 },
848 "foreignKeys": {},
849 "compositePrimaryKeys": {},
850 "uniqueConstraints": {},
851 "checkConstraints": {}
852 },
853 "sources": {
854 "name": "sources",
855 "columns": {
856 "workspace_id": {
857 "name": "workspace_id",
858 "type": "text",
859 "primaryKey": false,
860 "notNull": true,
861 "autoincrement": false
862 },
863 "source_id": {
864 "name": "source_id",
865 "type": "text",
866 "primaryKey": false,
867 "notNull": true,
868 "autoincrement": false
869 },
870 "name": {
871 "name": "name",
872 "type": "text",
873 "primaryKey": false,
874 "notNull": true,
875 "autoincrement": false
876 },
877 "kind": {
878 "name": "kind",
879 "type": "text",
880 "primaryKey": false,
881 "notNull": true,
882 "autoincrement": false
883 },
884 "endpoint": {
885 "name": "endpoint",
886 "type": "text",
887 "primaryKey": false,
888 "notNull": true,
889 "autoincrement": false
890 },
891 "status": {
892 "name": "status",
893 "type": "text",
894 "primaryKey": false,
895 "notNull": true,
896 "autoincrement": false
897 },
898 "enabled": {
899 "name": "enabled",
900 "type": "integer",
901 "primaryKey": false,
902 "notNull": true,
903 "autoincrement": false
904 },
905 "config_json": {
906 "name": "config_json",
907 "type": "text",
908 "primaryKey": false,
909 "notNull": true,
910 "autoincrement": false
911 },
912 "source_hash": {
913 "name": "source_hash",
914 "type": "text",
915 "primaryKey": false,
916 "notNull": false,
917 "autoincrement": false
918 },
919 "last_error": {
920 "name": "last_error",
921 "type": "text",
922 "primaryKey": false,
923 "notNull": false,
924 "autoincrement": false
925 },
926 "created_at": {
927 "name": "created_at",
928 "type": "integer",
929 "primaryKey": false,
930 "notNull": true,
931 "autoincrement": false
932 },
933 "updated_at": {
934 "name": "updated_at",
935 "type": "integer",
936 "primaryKey": false,
937 "notNull": true,
938 "autoincrement": false
939 }
940 },
941 "indexes": {
942 "sources_workspace_name_idx": {
943 "name": "sources_workspace_name_idx",
944 "columns": [
945 "workspace_id",
946 "name"
947 ],
948 "isUnique": false
949 }
950 },
951 "foreignKeys": {},
952 "compositePrimaryKeys": {
953 "sources_workspace_id_source_id_pk": {
954 "columns": [
955 "workspace_id",
956 "source_id"
957 ],
958 "name": "sources_workspace_id_source_id_pk"
959 }
960 },
961 "uniqueConstraints": {},
962 "checkConstraints": {}
963 },
964 "storage_instances": {
965 "name": "storage_instances",
966 "columns": {
967 "id": {
968 "name": "id",
969 "type": "text",
970 "primaryKey": true,
971 "notNull": true,
972 "autoincrement": false
973 },
974 "scope_type": {
975 "name": "scope_type",
976 "type": "text",
977 "primaryKey": false,
978 "notNull": true,
979 "autoincrement": false
980 },
981 "durability": {
982 "name": "durability",
983 "type": "text",
984 "primaryKey": false,
985 "notNull": true,
986 "autoincrement": false
987 },
988 "status": {
989 "name": "status",
990 "type": "text",
991 "primaryKey": false,
992 "notNull": true,
993 "autoincrement": false
994 },
995 "provider": {
996 "name": "provider",
997 "type": "text",
998 "primaryKey": false,
999 "notNull": true,
1000 "autoincrement": false
1001 },
1002 "backend_key": {
1003 "name": "backend_key",
1004 "type": "text",
1005 "primaryKey": false,
1006 "notNull": true,
1007 "autoincrement": false
1008 },
1009 "organization_id": {
1010 "name": "organization_id",
1011 "type": "text",
1012 "primaryKey": false,
1013 "notNull": true,
1014 "autoincrement": false
1015 },
1016 "workspace_id": {
1017 "name": "workspace_id",
1018 "type": "text",
1019 "primaryKey": false,
1020 "notNull": false,
1021 "autoincrement": false
1022 },
1023 "account_id": {
1024 "name": "account_id",
1025 "type": "text",
1026 "primaryKey": false,
1027 "notNull": false,
1028 "autoincrement": false
1029 },
1030 "created_by_account_id": {
1031 "name": "created_by_account_id",
1032 "type": "text",
1033 "primaryKey": false,
1034 "notNull": false,
1035 "autoincrement": false
1036 },
1037 "purpose": {
1038 "name": "purpose",
1039 "type": "text",
1040 "primaryKey": false,
1041 "notNull": false,
1042 "autoincrement": false
1043 },
1044 "size_bytes": {
1045 "name": "size_bytes",
1046 "type": "integer",
1047 "primaryKey": false,
1048 "notNull": false,
1049 "autoincrement": false
1050 },
1051 "file_count": {
1052 "name": "file_count",
1053 "type": "integer",
1054 "primaryKey": false,
1055 "notNull": false,
1056 "autoincrement": false
1057 },
1058 "created_at": {
1059 "name": "created_at",
1060 "type": "integer",
1061 "primaryKey": false,
1062 "notNull": true,
1063 "autoincrement": false
1064 },
1065 "updated_at": {
1066 "name": "updated_at",
1067 "type": "integer",
1068 "primaryKey": false,
1069 "notNull": true,
1070 "autoincrement": false
1071 },
1072 "last_seen_at": {
1073 "name": "last_seen_at",
1074 "type": "integer",
1075 "primaryKey": false,
1076 "notNull": true,
1077 "autoincrement": false
1078 },
1079 "closed_at": {
1080 "name": "closed_at",
1081 "type": "integer",
1082 "primaryKey": false,
1083 "notNull": false,
1084 "autoincrement": false
1085 },
1086 "expires_at": {
1087 "name": "expires_at",
1088 "type": "integer",
1089 "primaryKey": false,
1090 "notNull": false,
1091 "autoincrement": false
1092 }
1093 },
1094 "indexes": {
1095 "storage_instances_org_idx": {
1096 "name": "storage_instances_org_idx",
1097 "columns": [
1098 "organization_id"
1099 ],
1100 "isUnique": false
1101 },
1102 "storage_instances_workspace_idx": {
1103 "name": "storage_instances_workspace_idx",
1104 "columns": [
1105 "workspace_id"
1106 ],
1107 "isUnique": false
1108 }
1109 },
1110 "foreignKeys": {},
1111 "compositePrimaryKeys": {},
1112 "uniqueConstraints": {},
1113 "checkConstraints": {}
1114 },
1115 "sync_states": {
1116 "name": "sync_states",
1117 "columns": {
1118 "id": {
1119 "name": "id",
1120 "type": "text",
1121 "primaryKey": true,
1122 "notNull": true,
1123 "autoincrement": false
1124 },
1125 "workspace_id": {
1126 "name": "workspace_id",
1127 "type": "text",
1128 "primaryKey": false,
1129 "notNull": true,
1130 "autoincrement": false
1131 },
1132 "target": {
1133 "name": "target",
1134 "type": "text",
1135 "primaryKey": false,
1136 "notNull": true,
1137 "autoincrement": false
1138 },
1139 "target_url": {
1140 "name": "target_url",
1141 "type": "text",
1142 "primaryKey": false,
1143 "notNull": true,
1144 "autoincrement": false
1145 },
1146 "linked_subject": {
1147 "name": "linked_subject",
1148 "type": "text",
1149 "primaryKey": false,
1150 "notNull": false,
1151 "autoincrement": false
1152 },
1153 "cursor": {
1154 "name": "cursor",
1155 "type": "text",
1156 "primaryKey": false,
1157 "notNull": false,
1158 "autoincrement": false
1159 },
1160 "last_sync_at": {
1161 "name": "last_sync_at",
1162 "type": "integer",
1163 "primaryKey": false,
1164 "notNull": false,
1165 "autoincrement": false
1166 },
1167 "updated_at": {
1168 "name": "updated_at",
1169 "type": "integer",
1170 "primaryKey": false,
1171 "notNull": true,
1172 "autoincrement": false
1173 }
1174 },
1175 "indexes": {
1176 "sync_states_workspace_idx": {
1177 "name": "sync_states_workspace_idx",
1178 "columns": [
1179 "workspace_id"
1180 ],
1181 "isUnique": false
1182 }
1183 },
1184 "foreignKeys": {},
1185 "compositePrimaryKeys": {},
1186 "uniqueConstraints": {},
1187 "checkConstraints": {}
1188 },
1189 "task_runs": {
1190 "name": "task_runs",
1191 "columns": {
1192 "id": {
1193 "name": "id",
1194 "type": "text",
1195 "primaryKey": true,
1196 "notNull": true,
1197 "autoincrement": false
1198 },
1199 "workspace_id": {
1200 "name": "workspace_id",
1201 "type": "text",
1202 "primaryKey": false,
1203 "notNull": true,
1204 "autoincrement": false
1205 },
1206 "account_id": {
1207 "name": "account_id",
1208 "type": "text",
1209 "primaryKey": false,
1210 "notNull": true,
1211 "autoincrement": false
1212 },
1213 "session_id": {
1214 "name": "session_id",
1215 "type": "text",
1216 "primaryKey": false,
1217 "notNull": true,
1218 "autoincrement": false
1219 },
1220 "runtime_id": {
1221 "name": "runtime_id",
1222 "type": "text",
1223 "primaryKey": false,
1224 "notNull": true,
1225 "autoincrement": false
1226 },
1227 "code_hash": {
1228 "name": "code_hash",
1229 "type": "text",
1230 "primaryKey": false,
1231 "notNull": true,
1232 "autoincrement": false
1233 },
1234 "status": {
1235 "name": "status",
1236 "type": "text",
1237 "primaryKey": false,
1238 "notNull": true,
1239 "autoincrement": false
1240 },
1241 "started_at": {
1242 "name": "started_at",
1243 "type": "integer",
1244 "primaryKey": false,
1245 "notNull": false,
1246 "autoincrement": false
1247 },
1248 "completed_at": {
1249 "name": "completed_at",
1250 "type": "integer",
1251 "primaryKey": false,
1252 "notNull": false,
1253 "autoincrement": false
1254 },
1255 "exit_code": {
1256 "name": "exit_code",
1257 "type": "integer",
1258 "primaryKey": false,
1259 "notNull": false,
1260 "autoincrement": false
1261 },
1262 "error": {
1263 "name": "error",
1264 "type": "text",
1265 "primaryKey": false,
1266 "notNull": false,
1267 "autoincrement": false
1268 }
1269 },
1270 "indexes": {
1271 "task_runs_workspace_idx": {
1272 "name": "task_runs_workspace_idx",
1273 "columns": [
1274 "workspace_id"
1275 ],
1276 "isUnique": false
1277 }
1278 },
1279 "foreignKeys": {},
1280 "compositePrimaryKeys": {},
1281 "uniqueConstraints": {},
1282 "checkConstraints": {}
1283 },
1284 "tool_artifacts": {
1285 "name": "tool_artifacts",
1286 "columns": {
1287 "id": {
1288 "name": "id",
1289 "type": "text",
1290 "primaryKey": false,
1291 "notNull": true,
1292 "autoincrement": false
1293 },
1294 "workspace_id": {
1295 "name": "workspace_id",
1296 "type": "text",
1297 "primaryKey": false,
1298 "notNull": true,
1299 "autoincrement": false
1300 },
1301 "source_id": {
1302 "name": "source_id",
1303 "type": "text",
1304 "primaryKey": false,
1305 "notNull": true,
1306 "autoincrement": false
1307 },
1308 "source_hash": {
1309 "name": "source_hash",
1310 "type": "text",
1311 "primaryKey": false,
1312 "notNull": true,
1313 "autoincrement": false
1314 },
1315 "tool_count": {
1316 "name": "tool_count",
1317 "type": "integer",
1318 "primaryKey": false,
1319 "notNull": true,
1320 "autoincrement": false
1321 },
1322 "manifest_json": {
1323 "name": "manifest_json",
1324 "type": "text",
1325 "primaryKey": false,
1326 "notNull": true,
1327 "autoincrement": false
1328 },
1329 "created_at": {
1330 "name": "created_at",
1331 "type": "integer",
1332 "primaryKey": false,
1333 "notNull": true,
1334 "autoincrement": false
1335 },
1336 "updated_at": {
1337 "name": "updated_at",
1338 "type": "integer",
1339 "primaryKey": false,
1340 "notNull": true,
1341 "autoincrement": false
1342 }
1343 },
1344 "indexes": {
1345 "tool_artifacts_id_idx": {
1346 "name": "tool_artifacts_id_idx",
1347 "columns": [
1348 "id"
1349 ],
1350 "isUnique": true
1351 }
1352 },
1353 "foreignKeys": {},
1354 "compositePrimaryKeys": {
1355 "tool_artifacts_workspace_id_source_id_pk": {
1356 "columns": [
1357 "workspace_id",
1358 "source_id"
1359 ],
1360 "name": "tool_artifacts_workspace_id_source_id_pk"
1361 }
1362 },
1363 "uniqueConstraints": {},
1364 "checkConstraints": {}
1365 },
1366 "workspaces": {
1367 "name": "workspaces",
1368 "columns": {
1369 "id": {
1370 "name": "id",
1371 "type": "text",
1372 "primaryKey": true,
1373 "notNull": true,
1374 "autoincrement": false
1375 },
1376 "organization_id": {
1377 "name": "organization_id",
1378 "type": "text",
1379 "primaryKey": false,
1380 "notNull": true,
1381 "autoincrement": false
1382 },
1383 "name": {
1384 "name": "name",
1385 "type": "text",
1386 "primaryKey": false,
1387 "notNull": true,
1388 "autoincrement": false
1389 },
1390 "created_by_account_id": {
1391 "name": "created_by_account_id",
1392 "type": "text",
1393 "primaryKey": false,
1394 "notNull": false,
1395 "autoincrement": false
1396 },
1397 "created_at": {
1398 "name": "created_at",
1399 "type": "integer",
1400 "primaryKey": false,
1401 "notNull": true,
1402 "autoincrement": false
1403 },
1404 "updated_at": {
1405 "name": "updated_at",
1406 "type": "integer",
1407 "primaryKey": false,
1408 "notNull": true,
1409 "autoincrement": false
1410 }
1411 },
1412 "indexes": {
1413 "workspaces_org_idx": {
1414 "name": "workspaces_org_idx",
1415 "columns": [
1416 "organization_id"
1417 ],
1418 "isUnique": false
1419 }
1420 },
1421 "foreignKeys": {},
1422 "compositePrimaryKeys": {},
1423 "uniqueConstraints": {},
1424 "checkConstraints": {}
1425 }
1426 },
1427 "views": {},
1428 "enums": {},
1429 "_meta": {
1430 "schemas": {},
1431 "tables": {},
1432 "columns": {}
1433 },
1434 "internal": {
1435 "indexes": {}
1436 }
1437}
packages/persistence-sql/drizzle/sqlite/meta/_journal.json+0−13
@@ -1,13 +0,0 @@
1{
2 "version": "7",
3 "dialect": "sqlite",
4 "entries": [
5 {
6 "idx": 0,
7 "version": "6",
8 "when": 1772506139541,
9 "tag": "0000_curved_raider",
10 "breakpoints": true
11 }
12 ]
13}
packages/persistence-sql/package.json+1−3
@@ -8,14 +8,12 @@ · 7 unmodified lines
8 "scripts": {8 "scripts": {
9 "typecheck": "bunx tsc --noEmit -p tsconfig.json",9 "typecheck": "bunx tsc --noEmit -p tsconfig.json",
10 "db:generate": "drizzle-kit generate --config drizzle.config.ts",10 "db:generate": "drizzle-kit generate --config drizzle.config.ts",
11 "db:generate:pg": "drizzle-kit generate --config drizzle.config.pg.ts",
12 "db:check": "drizzle-kit check --config drizzle.config.ts",11 "db:check": "drizzle-kit check --config drizzle.config.ts",
13 "db:check:pg": "drizzle-kit check --config drizzle.config.pg.ts",
14 "db:migrate": "drizzle-kit migrate --config drizzle.config.ts",12 "db:migrate": "drizzle-kit migrate --config drizzle.config.ts",
15 "db:migrate:pg": "drizzle-kit migrate --config drizzle.config.pg.ts",
16 "db:studio": "drizzle-kit studio --config drizzle.config.ts"13 "db:studio": "drizzle-kit studio --config drizzle.config.ts"
17 },14 },
18 "dependencies": {15 "dependencies": {
16 "@electric-sql/pglite": "^0.3.10",
19 "@executor-v2/persistence-ports": "workspace:*",17 "@executor-v2/persistence-ports": "workspace:*",
20 "@executor-v2/schema": "workspace:*",18 "@executor-v2/schema": "workspace:*",
21 "drizzle-orm": "^0.44.7",19 "drizzle-orm": "^0.44.7",
packages/persistence-sql/src/index.ts+7−7
@@ -25,8 +25,8 @@ import { createRowOperations } from "./row-operations"; · 24 unmodified lines
25import { createRowsEffectApi } from "./rows-effect-api";25import { createRowsEffectApi } from "./rows-effect-api";
26import {26import {
27 createDrizzleContext,27 createDrizzleContext,
28 createPGliteAdapter,
28 createPostgresAdapter,29 createPostgresAdapter,
29 createSqliteAdapter,
30 runMigrations,30 runMigrations,
31 type SqlBackend,31 type SqlBackend,
32} from "./sql-internals";32} from "./sql-internals";
@@ -34,7 +34,7 @@ import { createSourceAndArtifactStores } from "./source-artifact-stores"; · 1 unmodified line
34 34
35export type SqlControlPlanePersistenceOptions = {35export type SqlControlPlanePersistenceOptions = {
36 databaseUrl?: string;36 databaseUrl?: string;
37 sqlitePath?: string;37 localDataDir?: string;
38 postgresApplicationName?: string;38 postgresApplicationName?: string;
39};39};
40 40
@@ -141,20 +141,20 @@ export const makeSqlControlPlanePersistence = ( · 100 unmodified lines
141 Effect.tryPromise({141 Effect.tryPromise({
142 try: async () => {142 try: async () => {
143 const databaseUrl = trim(options.databaseUrl);143 const databaseUrl = trim(options.databaseUrl);
144 const sqlitePath = path.resolve(144 const localDataDir = path.resolve(
145 options.sqlitePath ?? ".executor-v2/control-plane.sqlite",145 options.localDataDir ?? ".executor-v2/control-plane-pgdata",
146 );146 );
147 const backend: SqlBackend =147 const backend: SqlBackend =
148 databaseUrl && (databaseUrl.startsWith("postgres://") || databaseUrl.startsWith("postgresql://"))148 databaseUrl && (databaseUrl.startsWith("postgres://") || databaseUrl.startsWith("postgresql://"))
149 ? "postgres"149 ? "postgres"
150 : "sqlite";150 : "pglite";
151 151
152 const adapter =152 const adapter =
153 backend === "postgres"153 backend === "postgres"
154 ? await createPostgresAdapter(databaseUrl!, trim(options.postgresApplicationName))154 ? await createPostgresAdapter(databaseUrl!, trim(options.postgresApplicationName))
155 : await createSqliteAdapter(sqlitePath);155 : await createPGliteAdapter(localDataDir);
156 156
157 await runMigrations(backend, adapter);157 await runMigrations(adapter);
158 const drizzleContext = createDrizzleContext(adapter);158 const drizzleContext = createDrizzleContext(adapter);
159 const { db, tables } = drizzleContext;159 const { db, tables } = drizzleContext;
160 160
packages/persistence-sql/src/schema-pg.ts+0−304
@@ -1,304 +0,0 @@
1import {
2 bigint,
3 boolean,
4 index,
5 pgTable,
6 primaryKey,
7 text,
8 uniqueIndex,
9} from "drizzle-orm/pg-core";
10
11import { tableNames } from "./schema";
12
13export const profileTable = pgTable(tableNames.profile, {
14 id: text("id").notNull().primaryKey(),
15 defaultWorkspaceId: text("default_workspace_id"),
16 displayName: text("display_name").notNull(),
17 runtimeMode: text("runtime_mode").notNull(),
18 createdAt: bigint("created_at", { mode: "number" }).notNull(),
19 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
20});
21
22export const organizationsTable = pgTable(tableNames.organizations, {
23 id: text("id").notNull().primaryKey(),
24 slug: text("slug").notNull(),
25 name: text("name").notNull(),
26 status: text("status").notNull(),
27 createdByAccountId: text("created_by_account_id"),
28 createdAt: bigint("created_at", { mode: "number" }).notNull(),
29 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
30});
31
32export const organizationMembershipsTable = pgTable(
33 tableNames.organizationMemberships,
34 {
35 id: text("id").notNull().primaryKey(),
36 organizationId: text("organization_id").notNull(),
37 accountId: text("account_id").notNull(),
38 role: text("role").notNull(),
39 status: text("status").notNull(),
40 billable: boolean("billable").notNull(),
41 invitedByAccountId: text("invited_by_account_id"),
42 joinedAt: bigint("joined_at", { mode: "number" }),
43 createdAt: bigint("created_at", { mode: "number" }).notNull(),
44 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
45 },
46 (table) => [
47 index("organization_memberships_org_idx").on(table.organizationId),
48 index("organization_memberships_account_idx").on(table.accountId),
49 ],
50);
51
52export const workspacesTable = pgTable(
53 tableNames.workspaces,
54 {
55 id: text("id").notNull().primaryKey(),
56 organizationId: text("organization_id").notNull(),
57 name: text("name").notNull(),
58 createdByAccountId: text("created_by_account_id"),
59 createdAt: bigint("created_at", { mode: "number" }).notNull(),
60 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
61 },
62 (table) => [
63 index("workspaces_org_idx").on(table.organizationId),
64 ],
65);
66
67export const sourcesTable = pgTable(
68 tableNames.sources,
69 {
70 workspaceId: text("workspace_id").notNull(),
71 sourceId: text("source_id").notNull(),
72 name: text("name").notNull(),
73 kind: text("kind").notNull(),
74 endpoint: text("endpoint").notNull(),
75 status: text("status").notNull(),
76 enabled: boolean("enabled").notNull(),
77 configJson: text("config_json").notNull(),
78 sourceHash: text("source_hash"),
79 lastError: text("last_error"),
80 createdAt: bigint("created_at", { mode: "number" }).notNull(),
81 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
82 },
83 (table) => [
84 primaryKey({
85 columns: [table.workspaceId, table.sourceId],
86 }),
87 index("sources_workspace_name_idx").on(table.workspaceId, table.name),
88 ],
89);
90
91export const toolArtifactsTable = pgTable(
92 tableNames.toolArtifacts,
93 {
94 id: text("id").notNull(),
95 workspaceId: text("workspace_id").notNull(),
96 sourceId: text("source_id").notNull(),
97 sourceHash: text("source_hash").notNull(),
98 toolCount: bigint("tool_count", { mode: "number" }).notNull(),
99 manifestJson: text("manifest_json").notNull(),
100 createdAt: bigint("created_at", { mode: "number" }).notNull(),
101 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
102 },
103 (table) => [
104 primaryKey({
105 columns: [table.workspaceId, table.sourceId],
106 }),
107 uniqueIndex("tool_artifacts_id_idx").on(table.id),
108 ],
109);
110
111export const authConnectionsTable = pgTable(
112 tableNames.authConnections,
113 {
114 id: text("id").notNull().primaryKey(),
115 organizationId: text("organization_id").notNull(),
116 workspaceId: text("workspace_id"),
117 accountId: text("account_id"),
118 ownerType: text("owner_type").notNull(),
119 strategy: text("strategy").notNull(),
120 displayName: text("display_name").notNull(),
121 status: text("status").notNull(),
122 statusReason: text("status_reason"),
123 lastAuthErrorClass: text("last_auth_error_class"),
124 metadataJson: text("metadata_json"),
125 additionalHeadersJson: text("additional_headers_json"),
126 createdByAccountId: text("created_by_account_id"),
127 createdAt: bigint("created_at", { mode: "number" }).notNull(),
128 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
129 lastUsedAt: bigint("last_used_at", { mode: "number" }),
130 },
131 (table) => [
132 index("auth_connections_org_idx").on(table.organizationId),
133 index("auth_connections_workspace_idx").on(table.workspaceId),
134 index("auth_connections_account_idx").on(table.accountId),
135 ],
136);
137
138export const sourceAuthBindingsTable = pgTable(
139 tableNames.sourceAuthBindings,
140 {
141 id: text("id").notNull().primaryKey(),
142 sourceId: text("source_id").notNull(),
143 connectionId: text("connection_id").notNull(),
144 organizationId: text("organization_id").notNull(),
145 workspaceId: text("workspace_id"),
146 accountId: text("account_id"),
147 scopeType: text("scope_type").notNull(),
148 selector: text("selector"),
149 enabled: boolean("enabled").notNull(),
150 createdAt: bigint("created_at", { mode: "number" }).notNull(),
151 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
152 },
153 (table) => [
154 index("source_auth_bindings_source_idx").on(table.sourceId),
155 index("source_auth_bindings_connection_idx").on(table.connectionId),
156 index("source_auth_bindings_org_idx").on(table.organizationId),
157 index("source_auth_bindings_workspace_idx").on(table.workspaceId),
158 index("source_auth_bindings_account_idx").on(table.accountId),
159 ],
160);
161
162export const authMaterialsTable = pgTable(
163 tableNames.authMaterials,
164 {
165 id: text("id").notNull().primaryKey(),
166 connectionId: text("connection_id").notNull(),
167 ciphertext: text("ciphertext").notNull(),
168 keyVersion: text("key_version").notNull(),
169 createdAt: bigint("created_at", { mode: "number" }).notNull(),
170 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
171 },
172 (table) => [
173 index("auth_materials_connection_idx").on(table.connectionId),
174 ],
175);
176
177export const oauthStatesTable = pgTable(
178 tableNames.oauthStates,
179 {
180 id: text("id").notNull().primaryKey(),
181 connectionId: text("connection_id").notNull(),
182 accessTokenCiphertext: text("access_token_ciphertext").notNull(),
183 refreshTokenCiphertext: text("refresh_token_ciphertext"),
184 keyVersion: text("key_version").notNull(),
185 expiresAt: bigint("expires_at", { mode: "number" }),
186 scope: text("scope"),
187 tokenType: text("token_type"),
188 issuer: text("issuer"),
189 refreshConfigJson: text("refresh_config_json"),
190 tokenVersion: bigint("token_version", { mode: "number" }).notNull(),
191 leaseHolder: text("lease_holder"),
192 leaseExpiresAt: bigint("lease_expires_at", { mode: "number" }),
193 leaseFence: bigint("lease_fence", { mode: "number" }).notNull(),
194 lastRefreshAt: bigint("last_refresh_at", { mode: "number" }),
195 lastRefreshErrorClass: text("last_refresh_error_class"),
196 lastRefreshError: text("last_refresh_error"),
197 reauthRequiredAt: bigint("reauth_required_at", { mode: "number" }),
198 createdAt: bigint("created_at", { mode: "number" }).notNull(),
199 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
200 },
201 (table) => [
202 index("oauth_states_connection_idx").on(table.connectionId),
203 ],
204);
205
206export const policiesTable = pgTable(
207 tableNames.policies,
208 {
209 id: text("id").notNull().primaryKey(),
210 workspaceId: text("workspace_id").notNull(),
211 toolPathPattern: text("tool_path_pattern").notNull(),
212 decision: text("decision").notNull(),
213 createdAt: bigint("created_at", { mode: "number" }).notNull(),
214 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
215 },
216 (table) => [
217 index("policies_workspace_idx").on(table.workspaceId),
218 ],
219);
220
221export const approvalsTable = pgTable(
222 tableNames.approvals,
223 {
224 id: text("id").notNull().primaryKey(),
225 workspaceId: text("workspace_id").notNull(),
226 taskRunId: text("task_run_id").notNull(),
227 callId: text("call_id").notNull(),
228 toolPath: text("tool_path").notNull(),
229 status: text("status").notNull(),
230 inputPreviewJson: text("input_preview_json").notNull(),
231 reason: text("reason"),
232 requestedAt: bigint("requested_at", { mode: "number" }).notNull(),
233 resolvedAt: bigint("resolved_at", { mode: "number" }),
234 },
235 (table) => [
236 index("approvals_workspace_idx").on(table.workspaceId),
237 index("approvals_task_run_idx").on(table.taskRunId),
238 ],
239);
240
241export const taskRunsTable = pgTable(
242 tableNames.taskRuns,
243 {
244 id: text("id").notNull().primaryKey(),
245 workspaceId: text("workspace_id").notNull(),
246 accountId: text("account_id").notNull(),
247 sessionId: text("session_id").notNull(),
248 runtimeId: text("runtime_id").notNull(),
249 codeHash: text("code_hash").notNull(),
250 status: text("status").notNull(),
251 startedAt: bigint("started_at", { mode: "number" }),
252 completedAt: bigint("completed_at", { mode: "number" }),
253 exitCode: bigint("exit_code", { mode: "number" }),
254 error: text("error"),
255 },
256 (table) => [
257 index("task_runs_workspace_idx").on(table.workspaceId),
258 ],
259);
260
261export const storageInstancesTable = pgTable(
262 tableNames.storageInstances,
263 {
264 id: text("id").notNull().primaryKey(),
265 scopeType: text("scope_type").notNull(),
266 durability: text("durability").notNull(),
267 status: text("status").notNull(),
268 provider: text("provider").notNull(),
269 backendKey: text("backend_key").notNull(),
270 organizationId: text("organization_id").notNull(),
271 workspaceId: text("workspace_id"),
272 accountId: text("account_id"),
273 createdByAccountId: text("created_by_account_id"),
274 purpose: text("purpose"),
275 sizeBytes: bigint("size_bytes", { mode: "number" }),
276 fileCount: bigint("file_count", { mode: "number" }),
277 createdAt: bigint("created_at", { mode: "number" }).notNull(),
278 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
279 lastSeenAt: bigint("last_seen_at", { mode: "number" }).notNull(),
280 closedAt: bigint("closed_at", { mode: "number" }),
281 expiresAt: bigint("expires_at", { mode: "number" }),
282 },
283 (table) => [
284 index("storage_instances_org_idx").on(table.organizationId),
285 index("storage_instances_workspace_idx").on(table.workspaceId),
286 ],
287);
288
289export const syncStatesTable = pgTable(
290 tableNames.syncStates,
291 {
292 id: text("id").notNull().primaryKey(),
293 workspaceId: text("workspace_id").notNull(),
294 target: text("target").notNull(),
295 targetUrl: text("target_url").notNull(),
296 linkedSubject: text("linked_subject"),
297 cursor: text("cursor"),
298 lastSyncAt: bigint("last_sync_at", { mode: "number" }),
299 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
300 },
301 (table) => [
302 index("sync_states_workspace_idx").on(table.workspaceId),
303 ],
304);
packages/persistence-sql/src/schema.ts+67−66
@@ -1,11 +1,12 @@
1import {1import {
2 bigint,
3 boolean,
2 index,4 index,
3 integer,5 pgTable,
4 primaryKey,6 primaryKey,
5 sqliteTable,
6 text,7 text,
7 uniqueIndex,8 uniqueIndex,
8} from "drizzle-orm/sqlite-core";9} from "drizzle-orm/pg-core";
9 10
10export const tableNames = {11export const tableNames = {
11 profile: "profile",12 profile: "profile",
@@ -25,26 +26,26 @@ export const tableNames = { · 13 unmodified lines
25 syncStates: "sync_states",26 syncStates: "sync_states",
26} as const;27} as const;
27 28
28export const profileTable = sqliteTable(tableNames.profile, {29export const profileTable = pgTable(tableNames.profile, {
29 id: text("id").notNull().primaryKey(),30 id: text("id").notNull().primaryKey(),
30 defaultWorkspaceId: text("default_workspace_id"),31 defaultWorkspaceId: text("default_workspace_id"),
31 displayName: text("display_name").notNull(),32 displayName: text("display_name").notNull(),
32 runtimeMode: text("runtime_mode").notNull(),33 runtimeMode: text("runtime_mode").notNull(),
33 createdAt: integer("created_at", { mode: "number" }).notNull(),34 createdAt: bigint("created_at", { mode: "number" }).notNull(),
34 updatedAt: integer("updated_at", { mode: "number" }).notNull(),35 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
35});36});
36 37
37export const organizationsTable = sqliteTable(tableNames.organizations, {38export const organizationsTable = pgTable(tableNames.organizations, {
38 id: text("id").notNull().primaryKey(),39 id: text("id").notNull().primaryKey(),
39 slug: text("slug").notNull(),40 slug: text("slug").notNull(),
40 name: text("name").notNull(),41 name: text("name").notNull(),
41 status: text("status").notNull(),42 status: text("status").notNull(),
42 createdByAccountId: text("created_by_account_id"),43 createdByAccountId: text("created_by_account_id"),
43 createdAt: integer("created_at", { mode: "number" }).notNull(),44 createdAt: bigint("created_at", { mode: "number" }).notNull(),
44 updatedAt: integer("updated_at", { mode: "number" }).notNull(),45 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
45});46});
46 47
47export const organizationMembershipsTable = sqliteTable(48export const organizationMembershipsTable = pgTable(
48 tableNames.organizationMemberships,49 tableNames.organizationMemberships,
49 {50 {
50 id: text("id").notNull().primaryKey(),51 id: text("id").notNull().primaryKey(),
@@ -52,11 +53,11 @@ export const organizationMembershipsTable = sqliteTable( · 1 unmodified line
52 accountId: text("account_id").notNull(),53 accountId: text("account_id").notNull(),
53 role: text("role").notNull(),54 role: text("role").notNull(),
54 status: text("status").notNull(),55 status: text("status").notNull(),
55 billable: integer("billable", { mode: "boolean" }).notNull(),56 billable: boolean("billable").notNull(),
56 invitedByAccountId: text("invited_by_account_id"),57 invitedByAccountId: text("invited_by_account_id"),
57 joinedAt: integer("joined_at", { mode: "number" }),58 joinedAt: bigint("joined_at", { mode: "number" }),
58 createdAt: integer("created_at", { mode: "number" }).notNull(),59 createdAt: bigint("created_at", { mode: "number" }).notNull(),
59 updatedAt: integer("updated_at", { mode: "number" }).notNull(),60 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
60 },61 },
61 (table) => [62 (table) => [
62 index("organization_memberships_org_idx").on(table.organizationId),63 index("organization_memberships_org_idx").on(table.organizationId),
@@ -64,22 +65,22 @@ export const organizationMembershipsTable = sqliteTable( · 1 unmodified line
64 ],65 ],
65);66);
66 67
67export const workspacesTable = sqliteTable(68export const workspacesTable = pgTable(
68 tableNames.workspaces,69 tableNames.workspaces,
69 {70 {
70 id: text("id").notNull().primaryKey(),71 id: text("id").notNull().primaryKey(),
71 organizationId: text("organization_id").notNull(),72 organizationId: text("organization_id").notNull(),
72 name: text("name").notNull(),73 name: text("name").notNull(),
73 createdByAccountId: text("created_by_account_id"),74 createdByAccountId: text("created_by_account_id"),
74 createdAt: integer("created_at", { mode: "number" }).notNull(),75 createdAt: bigint("created_at", { mode: "number" }).notNull(),
75 updatedAt: integer("updated_at", { mode: "number" }).notNull(),76 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
76 },77 },
77 (table) => [78 (table) => [
78 index("workspaces_org_idx").on(table.organizationId),79 index("workspaces_org_idx").on(table.organizationId),
79 ],80 ],
80);81);
81 82
82export const sourcesTable = sqliteTable(83export const sourcesTable = pgTable(
83 tableNames.sources,84 tableNames.sources,
84 {85 {
85 workspaceId: text("workspace_id").notNull(),86 workspaceId: text("workspace_id").notNull(),
@@ -88,12 +89,12 @@ export const sourcesTable = sqliteTable( · 2 unmodified lines
88 kind: text("kind").notNull(),89 kind: text("kind").notNull(),
89 endpoint: text("endpoint").notNull(),90 endpoint: text("endpoint").notNull(),
90 status: text("status").notNull(),91 status: text("status").notNull(),
91 enabled: integer("enabled", { mode: "boolean" }).notNull(),92 enabled: boolean("enabled").notNull(),
92 configJson: text("config_json").notNull(),93 configJson: text("config_json").notNull(),
93 sourceHash: text("source_hash"),94 sourceHash: text("source_hash"),
94 lastError: text("last_error"),95 lastError: text("last_error"),
95 createdAt: integer("created_at", { mode: "number" }).notNull(),96 createdAt: bigint("created_at", { mode: "number" }).notNull(),
96 updatedAt: integer("updated_at", { mode: "number" }).notNull(),97 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
97 },98 },
98 (table) => [99 (table) => [
99 primaryKey({100 primaryKey({
@@ -103,17 +104,17 @@ export const sourcesTable = sqliteTable( · 3 unmodified lines
103 ],104 ],
104);105);
105 106
106export const toolArtifactsTable = sqliteTable(107export const toolArtifactsTable = pgTable(
107 tableNames.toolArtifacts,108 tableNames.toolArtifacts,
108 {109 {
109 id: text("id").notNull(),110 id: text("id").notNull(),
110 workspaceId: text("workspace_id").notNull(),111 workspaceId: text("workspace_id").notNull(),
111 sourceId: text("source_id").notNull(),112 sourceId: text("source_id").notNull(),
112 sourceHash: text("source_hash").notNull(),113 sourceHash: text("source_hash").notNull(),
113 toolCount: integer("tool_count", { mode: "number" }).notNull(),114 toolCount: bigint("tool_count", { mode: "number" }).notNull(),
114 manifestJson: text("manifest_json").notNull(),115 manifestJson: text("manifest_json").notNull(),
115 createdAt: integer("created_at", { mode: "number" }).notNull(),116 createdAt: bigint("created_at", { mode: "number" }).notNull(),
116 updatedAt: integer("updated_at", { mode: "number" }).notNull(),117 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
117 },118 },
118 (table) => [119 (table) => [
119 primaryKey({120 primaryKey({
@@ -123,7 +124,7 @@ export const toolArtifactsTable = sqliteTable( · 3 unmodified lines
123 ],124 ],
124);125);
125 126
126export const authConnectionsTable = sqliteTable(127export const authConnectionsTable = pgTable(
127 tableNames.authConnections,128 tableNames.authConnections,
128 {129 {
129 id: text("id").notNull().primaryKey(),130 id: text("id").notNull().primaryKey(),
@@ -139,9 +140,9 @@ export const authConnectionsTable = sqliteTable( · 9 unmodified lines
139 metadataJson: text("metadata_json"),140 metadataJson: text("metadata_json"),
140 additionalHeadersJson: text("additional_headers_json"),141 additionalHeadersJson: text("additional_headers_json"),
141 createdByAccountId: text("created_by_account_id"),142 createdByAccountId: text("created_by_account_id"),
142 createdAt: integer("created_at", { mode: "number" }).notNull(),143 createdAt: bigint("created_at", { mode: "number" }).notNull(),
143 updatedAt: integer("updated_at", { mode: "number" }).notNull(),144 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
144 lastUsedAt: integer("last_used_at", { mode: "number" }),145 lastUsedAt: bigint("last_used_at", { mode: "number" }),
145 },146 },
146 (table) => [147 (table) => [
147 index("auth_connections_org_idx").on(table.organizationId),148 index("auth_connections_org_idx").on(table.organizationId),
@@ -150,7 +151,7 @@ export const authConnectionsTable = sqliteTable( · 2 unmodified lines
150 ],151 ],
151);152);
152 153
153export const sourceAuthBindingsTable = sqliteTable(154export const sourceAuthBindingsTable = pgTable(
154 tableNames.sourceAuthBindings,155 tableNames.sourceAuthBindings,
155 {156 {
156 id: text("id").notNull().primaryKey(),157 id: text("id").notNull().primaryKey(),
@@ -161,9 +162,9 @@ export const sourceAuthBindingsTable = sqliteTable( · 4 unmodified lines
161 accountId: text("account_id"),162 accountId: text("account_id"),
162 scopeType: text("scope_type").notNull(),163 scopeType: text("scope_type").notNull(),
163 selector: text("selector"),164 selector: text("selector"),
164 enabled: integer("enabled", { mode: "boolean" }).notNull(),165 enabled: boolean("enabled").notNull(),
165 createdAt: integer("created_at", { mode: "number" }).notNull(),166 createdAt: bigint("created_at", { mode: "number" }).notNull(),
166 updatedAt: integer("updated_at", { mode: "number" }).notNull(),167 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
167 },168 },
168 (table) => [169 (table) => [
169 index("source_auth_bindings_source_idx").on(table.sourceId),170 index("source_auth_bindings_source_idx").on(table.sourceId),
@@ -174,22 +175,22 @@ export const sourceAuthBindingsTable = sqliteTable( · 4 unmodified lines
174 ],175 ],
175);176);
176 177
177export const authMaterialsTable = sqliteTable(178export const authMaterialsTable = pgTable(
178 tableNames.authMaterials,179 tableNames.authMaterials,
179 {180 {
180 id: text("id").notNull().primaryKey(),181 id: text("id").notNull().primaryKey(),
181 connectionId: text("connection_id").notNull(),182 connectionId: text("connection_id").notNull(),
182 ciphertext: text("ciphertext").notNull(),183 ciphertext: text("ciphertext").notNull(),
183 keyVersion: text("key_version").notNull(),184 keyVersion: text("key_version").notNull(),
184 createdAt: integer("created_at", { mode: "number" }).notNull(),185 createdAt: bigint("created_at", { mode: "number" }).notNull(),
185 updatedAt: integer("updated_at", { mode: "number" }).notNull(),186 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
186 },187 },
187 (table) => [188 (table) => [
188 index("auth_materials_connection_idx").on(table.connectionId),189 index("auth_materials_connection_idx").on(table.connectionId),
189 ],190 ],
190);191);
191 192
192export const oauthStatesTable = sqliteTable(193export const oauthStatesTable = pgTable(
193 tableNames.oauthStates,194 tableNames.oauthStates,
194 {195 {
195 id: text("id").notNull().primaryKey(),196 id: text("id").notNull().primaryKey(),
@@ -197,43 +198,43 @@ export const oauthStatesTable = sqliteTable( · 1 unmodified line
197 accessTokenCiphertext: text("access_token_ciphertext").notNull(),198 accessTokenCiphertext: text("access_token_ciphertext").notNull(),
198 refreshTokenCiphertext: text("refresh_token_ciphertext"),199 refreshTokenCiphertext: text("refresh_token_ciphertext"),
199 keyVersion: text("key_version").notNull(),200 keyVersion: text("key_version").notNull(),
200 expiresAt: integer("expires_at", { mode: "number" }),201 expiresAt: bigint("expires_at", { mode: "number" }),
201 scope: text("scope"),202 scope: text("scope"),
202 tokenType: text("token_type"),203 tokenType: text("token_type"),
203 issuer: text("issuer"),204 issuer: text("issuer"),
204 refreshConfigJson: text("refresh_config_json"),205 refreshConfigJson: text("refresh_config_json"),
205 tokenVersion: integer("token_version", { mode: "number" }).notNull(),206 tokenVersion: bigint("token_version", { mode: "number" }).notNull(),
206 leaseHolder: text("lease_holder"),207 leaseHolder: text("lease_holder"),
207 leaseExpiresAt: integer("lease_expires_at", { mode: "number" }),208 leaseExpiresAt: bigint("lease_expires_at", { mode: "number" }),
208 leaseFence: integer("lease_fence", { mode: "number" }).notNull(),209 leaseFence: bigint("lease_fence", { mode: "number" }).notNull(),
209 lastRefreshAt: integer("last_refresh_at", { mode: "number" }),210 lastRefreshAt: bigint("last_refresh_at", { mode: "number" }),
210 lastRefreshErrorClass: text("last_refresh_error_class"),211 lastRefreshErrorClass: text("last_refresh_error_class"),
211 lastRefreshError: text("last_refresh_error"),212 lastRefreshError: text("last_refresh_error"),
212 reauthRequiredAt: integer("reauth_required_at", { mode: "number" }),213 reauthRequiredAt: bigint("reauth_required_at", { mode: "number" }),
213 createdAt: integer("created_at", { mode: "number" }).notNull(),214 createdAt: bigint("created_at", { mode: "number" }).notNull(),
214 updatedAt: integer("updated_at", { mode: "number" }).notNull(),215 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
215 },216 },
216 (table) => [217 (table) => [
217 index("oauth_states_connection_idx").on(table.connectionId),218 index("oauth_states_connection_idx").on(table.connectionId),
218 ],219 ],
219);220);
220 221
221export const policiesTable = sqliteTable(222export const policiesTable = pgTable(
222 tableNames.policies,223 tableNames.policies,
223 {224 {
224 id: text("id").notNull().primaryKey(),225 id: text("id").notNull().primaryKey(),
225 workspaceId: text("workspace_id").notNull(),226 workspaceId: text("workspace_id").notNull(),
226 toolPathPattern: text("tool_path_pattern").notNull(),227 toolPathPattern: text("tool_path_pattern").notNull(),
227 decision: text("decision").notNull(),228 decision: text("decision").notNull(),
228 createdAt: integer("created_at", { mode: "number" }).notNull(),229 createdAt: bigint("created_at", { mode: "number" }).notNull(),
229 updatedAt: integer("updated_at", { mode: "number" }).notNull(),230 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
230 },231 },
231 (table) => [232 (table) => [
232 index("policies_workspace_idx").on(table.workspaceId),233 index("policies_workspace_idx").on(table.workspaceId),
233 ],234 ],
234);235);
235 236
236export const approvalsTable = sqliteTable(237export const approvalsTable = pgTable(
237 tableNames.approvals,238 tableNames.approvals,
238 {239 {
239 id: text("id").notNull().primaryKey(),240 id: text("id").notNull().primaryKey(),
@@ -244,8 +245,8 @@ export const approvalsTable = sqliteTable( · 4 unmodified lines
244 status: text("status").notNull(),245 status: text("status").notNull(),
245 inputPreviewJson: text("input_preview_json").notNull(),246 inputPreviewJson: text("input_preview_json").notNull(),
246 reason: text("reason"),247 reason: text("reason"),
247 requestedAt: integer("requested_at", { mode: "number" }).notNull(),248 requestedAt: bigint("requested_at", { mode: "number" }).notNull(),
248 resolvedAt: integer("resolved_at", { mode: "number" }),249 resolvedAt: bigint("resolved_at", { mode: "number" }),
249 },250 },
250 (table) => [251 (table) => [
251 index("approvals_workspace_idx").on(table.workspaceId),252 index("approvals_workspace_idx").on(table.workspaceId),
@@ -253,7 +254,7 @@ export const approvalsTable = sqliteTable( · 1 unmodified line
253 ],254 ],
254);255);
255 256
256export const taskRunsTable = sqliteTable(257export const taskRunsTable = pgTable(
257 tableNames.taskRuns,258 tableNames.taskRuns,
258 {259 {
259 id: text("id").notNull().primaryKey(),260 id: text("id").notNull().primaryKey(),
@@ -263,9 +264,9 @@ export const taskRunsTable = sqliteTable( · 3 unmodified lines
263 runtimeId: text("runtime_id").notNull(),264 runtimeId: text("runtime_id").notNull(),
264 codeHash: text("code_hash").notNull(),265 codeHash: text("code_hash").notNull(),
265 status: text("status").notNull(),266 status: text("status").notNull(),
266 startedAt: integer("started_at", { mode: "number" }),267 startedAt: bigint("started_at", { mode: "number" }),
267 completedAt: integer("completed_at", { mode: "number" }),268 completedAt: bigint("completed_at", { mode: "number" }),
268 exitCode: integer("exit_code", { mode: "number" }),269 exitCode: bigint("exit_code", { mode: "number" }),
269 error: text("error"),270 error: text("error"),
270 },271 },
271 (table) => [272 (table) => [
@@ -273,7 +274,7 @@ export const taskRunsTable = sqliteTable( · 1 unmodified line
273 ],274 ],
274);275);
275 276
276export const storageInstancesTable = sqliteTable(277export const storageInstancesTable = pgTable(
277 tableNames.storageInstances,278 tableNames.storageInstances,
278 {279 {
279 id: text("id").notNull().primaryKey(),280 id: text("id").notNull().primaryKey(),
@@ -287,13 +288,13 @@ export const storageInstancesTable = sqliteTable( · 7 unmodified lines
287 accountId: text("account_id"),288 accountId: text("account_id"),
288 createdByAccountId: text("created_by_account_id"),289 createdByAccountId: text("created_by_account_id"),
289 purpose: text("purpose"),290 purpose: text("purpose"),
290 sizeBytes: integer("size_bytes", { mode: "number" }),291 sizeBytes: bigint("size_bytes", { mode: "number" }),
291 fileCount: integer("file_count", { mode: "number" }),292 fileCount: bigint("file_count", { mode: "number" }),
292 createdAt: integer("created_at", { mode: "number" }).notNull(),293 createdAt: bigint("created_at", { mode: "number" }).notNull(),
293 updatedAt: integer("updated_at", { mode: "number" }).notNull(),294 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
294 lastSeenAt: integer("last_seen_at", { mode: "number" }).notNull(),295 lastSeenAt: bigint("last_seen_at", { mode: "number" }).notNull(),
295 closedAt: integer("closed_at", { mode: "number" }),296 closedAt: bigint("closed_at", { mode: "number" }),
296 expiresAt: integer("expires_at", { mode: "number" }),297 expiresAt: bigint("expires_at", { mode: "number" }),
297 },298 },
298 (table) => [299 (table) => [
299 index("storage_instances_org_idx").on(table.organizationId),300 index("storage_instances_org_idx").on(table.organizationId),
@@ -301,7 +302,7 @@ export const storageInstancesTable = sqliteTable( · 1 unmodified line
301 ],302 ],
302);303);
303 304
304export const syncStatesTable = sqliteTable(305export const syncStatesTable = pgTable(
305 tableNames.syncStates,306 tableNames.syncStates,
306 {307 {
307 id: text("id").notNull().primaryKey(),308 id: text("id").notNull().primaryKey(),
@@ -310,8 +311,8 @@ export const syncStatesTable = sqliteTable( · 2 unmodified lines
310 targetUrl: text("target_url").notNull(),311 targetUrl: text("target_url").notNull(),
311 linkedSubject: text("linked_subject"),312 linkedSubject: text("linked_subject"),
312 cursor: text("cursor"),313 cursor: text("cursor"),
313 lastSyncAt: integer("last_sync_at", { mode: "number" }),314 lastSyncAt: bigint("last_sync_at", { mode: "number" }),
314 updatedAt: integer("updated_at", { mode: "number" }).notNull(),315 updatedAt: bigint("updated_at", { mode: "number" }).notNull(),
315 },316 },
316 (table) => [317 (table) => [
317 index("sync_states_workspace_idx").on(table.workspaceId),318 index("sync_states_workspace_idx").on(table.workspaceId),
packages/persistence-sql/src/sql-internals.ts+306−0
@@ -0,0 +1,306 @@
1import { PGlite } from "@electric-sql/pglite";
2import { drizzle as drizzlePgProxy } from "drizzle-orm/pg-proxy";
3import { migrate as migratePgProxy } from "drizzle-orm/pg-proxy/migrator";
4import { existsSync } from "node:fs";
5import { mkdir } from "node:fs/promises";
6import * as path from "node:path";
7import { fileURLToPath } from "node:url";
8import postgres, { type Sql } from "postgres";
9
10import {
11 approvalsTable,
12 authConnectionsTable,
13 authMaterialsTable,
14 oauthStatesTable,
15 organizationMembershipsTable,
16 organizationsTable,
17 policiesTable,
18 profileTable,
19 sourceAuthBindingsTable,
20 sourcesTable,
21 storageInstancesTable,
22 syncStatesTable,
23 taskRunsTable,
24 toolArtifactsTable,
25 workspacesTable,
26} from "./schema";
27
28export type SqlBackend = "pglite" | "postgres";
29type SqlRow = Record<string, unknown>;
30
31export type SqlAdapter = {
32 readonly backend: SqlBackend;
33 query: <TRow extends SqlRow = SqlRow>(
34 statement: string,
35 args?: ReadonlyArray<unknown>,
36 ) => Promise<Array<TRow>>;
37 execute: (statement: string, args?: ReadonlyArray<unknown>) => Promise<void>;
38 transaction: <A>(run: (transaction: SqlAdapter) => Promise<A>) => Promise<A>;
39 close: () => Promise<void>;
40};
41
42const withPostgresPlaceholders = (statement: string): string => {
43 let index = 0;
44 return statement.replace(/\?/g, () => {
45 index += 1;
46 return `$${index}`;
47 });
48};
49
50const toPostgresStatement = (statement: string): string => withPostgresPlaceholders(statement);
51
52const makePGliteTransaction = (
53 execute: (statement: string, args?: ReadonlyArray<unknown>) => Promise<void>,
54 query: <TRow extends SqlRow = SqlRow>(
55 statement: string,
56 args?: ReadonlyArray<unknown>,
57 ) => Promise<Array<TRow>>,
58): SqlAdapter["transaction"] =>
59 async <A>(run: (transactionAdapter: SqlAdapter) => Promise<A>): Promise<A> => {
60 await execute("BEGIN");
61
62 try {
63 const adapter: SqlAdapter = {
64 backend: "pglite",
65 query,
66 execute,
67 transaction: async (nestedRun) => nestedRun(adapter),
68 close: async () => {},
69 };
70
71 const result = await run(adapter);
72 await execute("COMMIT");
73 return result;
74 } catch (error) {
75 try {
76 await execute("ROLLBACK");
77 } catch {
78 // ignore rollback failure after original error
79 }
80
81 throw error;
82 }
83 };
84
85export const createPGliteAdapter = async (localDataDir: string): Promise<SqlAdapter> => {
86 const resolvedDataDir = path.resolve(localDataDir);
87 await mkdir(path.dirname(resolvedDataDir), { recursive: true });
88
89 const db = new PGlite(resolvedDataDir);
90
91 const query = async <TRow extends SqlRow = SqlRow>(
92 statement: string,
93 args: ReadonlyArray<unknown> = [],
94 ): Promise<Array<TRow>> => {
95 const result = await db.query(toPostgresStatement(statement), [...args]);
96 return result.rows as Array<TRow>;
97 };
98
99 const execute = async (
100 statement: string,
101 args: ReadonlyArray<unknown> = [],
102 ): Promise<void> => {
103 await db.query(toPostgresStatement(statement), [...args]);
104 };
105
106 return {
107 backend: "pglite",
108 query,
109 execute,
110 transaction: makePGliteTransaction(execute, query),
111 close: async () => {
112 await db.close();
113 },
114 };
115};
116
117export const createPostgresAdapter = async (
118 databaseUrl: string,
119 applicationName: string | undefined,
120): Promise<SqlAdapter> => {
121 const client = postgres(databaseUrl, {
122 prepare: false,
123 max: 10,
124 ...(applicationName ? { connection: { application_name: applicationName } } : {}),
125 });
126
127 type UnsafeRunner = {
128 unsafe: Sql["unsafe"];
129 };
130
131 const toPostgresParams = (
132 args: ReadonlyArray<unknown>,
133 ): Array<postgres.ParameterOrJSON<never>> =>
134 args as unknown as Array<postgres.ParameterOrJSON<never>>;
135
136 const queryWith = async <TRow extends SqlRow = SqlRow>(
137 runner: UnsafeRunner,
138 statement: string,
139 args: ReadonlyArray<unknown> = [],
140 ): Promise<Array<TRow>> =>
141 (await runner.unsafe(
142 toPostgresStatement(statement),
143 toPostgresParams(args),
144 )) as unknown as Array<TRow>;
145
146 const executeWith = async (
147 runner: UnsafeRunner,
148 statement: string,
149 args: ReadonlyArray<unknown> = [],
150 ): Promise<void> => {
151 await runner.unsafe(
152 toPostgresStatement(statement),
153 toPostgresParams(args),
154 );
155 };
156
157 const adapter: SqlAdapter = {
158 backend: "postgres",
159 query: (statement, args = []) => queryWith(client, statement, args),
160 execute: (statement, args = []) => executeWith(client, statement, args),
161 transaction: async <A>(run: (transaction: SqlAdapter) => Promise<A>) => {
162 const result = await client.begin(async (transactionClient) => {
163 const runner: UnsafeRunner = transactionClient;
164 const transactionAdapter: SqlAdapter = {
165 backend: "postgres",
166 query: (statement, args = []) => queryWith(runner, statement, args),
167 execute: (statement, args = []) => executeWith(runner, statement, args),
168 transaction: async (nestedRun) => nestedRun(transactionAdapter),
169 close: async () => {},
170 };
171
172 return run(transactionAdapter);
173 });
174
175 return result as A;
176 },
177 close: async () => {
178 await client.end({ timeout: 5 });
179 },
180 };
181
182 return adapter;
183};
184
185const resolveDrizzleMigrationsFolder = (): string => {
186 const moduleDir = path.dirname(fileURLToPath(import.meta.url));
187 const packageRoot = path.resolve(moduleDir, "..");
188 const cwd = process.cwd();
189 const candidates = [
190 path.resolve(packageRoot, "drizzle"),
191 path.resolve(cwd, "packages/persistence-sql/drizzle"),
192 path.resolve(cwd, "../packages/persistence-sql/drizzle"),
193 path.resolve(cwd, "../../packages/persistence-sql/drizzle"),
194 ];
195
196 for (const candidate of candidates) {
197 if (existsSync(path.join(candidate, "meta", "_journal.json"))) {
198 return candidate;
199 }
200 }
201
202 throw new Error("Unable to resolve drizzle migrations folder");
203};
204
205const runMigrationQueries = async (
206 adapter: SqlAdapter,
207 queries: ReadonlyArray<string>,
208): Promise<void> => {
209 for (const query of queries) {
210 const statement = query.trim();
211 if (statement.length === 0) {
212 continue;
213 }
214
215 await adapter.execute(statement);
216 }
217};
218
219const toProxyRow = (row: unknown): unknown => {
220 if (Array.isArray(row) || row === null || row === undefined) {
221 return row;
222 }
223
224 if (typeof row === "object") {
225 return Object.values(row as Record<string, unknown>);
226 }
227
228 return row;
229};
230
231const normalizeProxyRows = (
232 method: "execute" | "all" | "values" | "get",
233 rows: ReadonlyArray<unknown>,
234): Array<unknown> => {
235 if (method === "get") {
236 const first = rows[0];
237 return first === undefined ? [] : [toProxyRow(first)];
238 }
239
240 return rows.map(toProxyRow);
241};
242
243const drizzleSchema = {
244 profileTable,
245 organizationsTable,
246 organizationMembershipsTable,
247 workspacesTable,
248 sourcesTable,
249 toolArtifactsTable,
250 authConnectionsTable,
251 sourceAuthBindingsTable,
252 authMaterialsTable,
253 oauthStatesTable,
254 policiesTable,
255 approvalsTable,
256 taskRunsTable,
257 storageInstancesTable,
258 syncStatesTable,
259};
260
261type DrizzleSchema = typeof drizzleSchema;
262type PostgresDrizzleDb = ReturnType<typeof drizzlePgProxy<DrizzleSchema>>;
263
264export type DrizzleDb = PostgresDrizzleDb;
265export type DrizzleTables = DrizzleSchema;
266
267export type DrizzleContext = {
268 db: DrizzleDb;
269 tables: DrizzleTables;
270};
271
272const createPostgresProxyDb = (adapter: SqlAdapter): PostgresDrizzleDb =>
273 drizzlePgProxy(
274 async (statement, params, method) => {
275 if (method === "execute") {
276 await adapter.execute(statement, params);
277 return { rows: [] };
278 }
279
280 const rows = await adapter.query(statement, params);
281 return {
282 rows: normalizeProxyRows(method, rows),
283 };
284 },
285 {
286 schema: drizzleSchema,
287 },
288 );
289
290export const createDrizzleContext = (adapter: SqlAdapter): DrizzleContext => ({
291 db: createPostgresProxyDb(adapter),
292 tables: drizzleSchema,
293});
294
295export const runMigrations = async (adapter: SqlAdapter): Promise<void> => {
296 const migrationDb = createPostgresProxyDb(adapter);
297 const migrationsFolder = resolveDrizzleMigrationsFolder();
298
299 await migratePgProxy(
300 migrationDb,
301 async (queries) => runMigrationQueries(adapter, queries),
302 {
303 migrationsFolder,
304 },
305 );
306};