@naisys/hub-database 3.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +84 -0
  2. package/dist/dbConfig.js +4 -0
  3. package/dist/generated/prisma/browser.js +17 -0
  4. package/dist/generated/prisma/client.js +34 -0
  5. package/dist/generated/prisma/commonInputTypes.js +10 -0
  6. package/dist/generated/prisma/enums.js +56 -0
  7. package/dist/generated/prisma/internal/class.js +49 -0
  8. package/dist/generated/prisma/internal/prismaNamespace.js +248 -0
  9. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +219 -0
  10. package/dist/generated/prisma/models/attachments.js +1 -0
  11. package/dist/generated/prisma/models/config_revisions.js +1 -0
  12. package/dist/generated/prisma/models/context_log.js +1 -0
  13. package/dist/generated/prisma/models/costs.js +1 -0
  14. package/dist/generated/prisma/models/hosts.js +1 -0
  15. package/dist/generated/prisma/models/mail_attachments.js +1 -0
  16. package/dist/generated/prisma/models/mail_messages.js +1 -0
  17. package/dist/generated/prisma/models/mail_recipients.js +1 -0
  18. package/dist/generated/prisma/models/models.js +1 -0
  19. package/dist/generated/prisma/models/run_session.js +1 -0
  20. package/dist/generated/prisma/models/schema_version.js +1 -0
  21. package/dist/generated/prisma/models/user_hosts.js +1 -0
  22. package/dist/generated/prisma/models/user_notifications.js +1 -0
  23. package/dist/generated/prisma/models/users.js +1 -0
  24. package/dist/generated/prisma/models/variables.js +1 -0
  25. package/dist/generated/prisma/models.js +1 -0
  26. package/dist/hubDatabaseService.js +38 -0
  27. package/dist/hubSessionService.js +112 -0
  28. package/dist/index.js +13 -0
  29. package/dist/prismaClient.js +21 -0
  30. package/package.json +42 -0
  31. package/prisma/migrations/20260223234341_init/migration.sql +237 -0
  32. package/prisma/migrations/20260225041145_add_host_restricted/migration.sql +20 -0
  33. package/prisma/migrations/20260227000000_add_host_type/migration.sql +21 -0
  34. package/prisma/migrations/20260302000000_make_from_user_id_not_null/migration.sql +37 -0
  35. package/prisma/migrations/20260302100000_add_cost_suspended_reason/migration.sql +2 -0
  36. package/prisma/migrations/20260305000000_make_participant_ids_not_null/migration.sql +31 -0
  37. package/prisma/migrations/20260306000000_add_host_last_ip/migration.sql +1 -0
  38. package/prisma/migrations/20260310000000_rename_participant_ids_to_participants/migration.sql +61 -0
  39. package/prisma/migrations/20260312000000_fix_empty_enum_strings/migration.sql +31 -0
  40. package/prisma/migrations/20260313000000_add_user_enabled/migration.sql +5 -0
  41. package/prisma/migrations/20260327000000_add_config_revisions/migration.sql +13 -0
  42. package/prisma/migrations/20260328000000_add_from_recipient_type/migration.sql +13 -0
  43. package/prisma/migrations/20260328100000_add_spend_limit_reset_at/migration.sql +1 -0
  44. package/prisma/migrations/20260328200000_add_budget_left/migration.sql +1 -0
  45. package/prisma/migrations/20260401000000_rename_workspace_to_tool/migration.sql +2 -0
  46. package/prisma/migrations/20260403000000_add_export_to_shell/migration.sql +2 -0
  47. package/prisma/migrations/20260404000000_add_attachment_public_id/migration.sql +8 -0
  48. package/prisma/migrations/migration_lock.toml +3 -0
  49. package/prisma/schema.prisma +307 -0
  50. package/prisma.config.ts +18 -0
@@ -0,0 +1,21 @@
1
+ import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
2
+ import { PrismaClient } from "./generated/prisma/client.js";
3
+ /**
4
+ * Create a Prisma client with a dynamic database path
5
+ * @param databasePath - Absolute path to the SQLite database file
6
+ * @returns Configured PrismaClient instance
7
+ */
8
+ export async function createPrismaClient(databasePath) {
9
+ const adapter = new PrismaBetterSqlite3({
10
+ url: `file:${databasePath}`,
11
+ timeout: 10_000, // Wait up to 10s for SQLite lock to be released
12
+ });
13
+ const prisma = new PrismaClient({ adapter });
14
+ // Enable WAL mode for better concurrent read/write performance
15
+ await prisma.$executeRawUnsafe("PRAGMA journal_mode=WAL");
16
+ // NORMAL is safe with WAL and avoids an extra fsync per commit
17
+ await prisma.$executeRawUnsafe("PRAGMA synchronous=NORMAL");
18
+ // SQLite doesn't enforce foreign keys by default — must be enabled per connection
19
+ await prisma.$executeRawUnsafe("PRAGMA foreign_keys=ON");
20
+ return prisma;
21
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@naisys/hub-database",
3
+ "version": "3.0.0-beta.10",
4
+ "type": "module",
5
+ "description": "[internal] Hub database schema and Prisma client for NAISYS",
6
+ "files": [
7
+ "dist",
8
+ "prisma.config.ts",
9
+ "prisma/schema.prisma",
10
+ "prisma/migrations",
11
+ "!dist/**/*.map",
12
+ "!dist/**/*.d.ts",
13
+ "!dist/**/*.d.ts.map"
14
+ ],
15
+ "scripts": {
16
+ "clean": "rimraf dist",
17
+ "prisma:migrate": "prisma migrate dev --create-only",
18
+ "prisma:generate": "prisma generate",
19
+ "prisma:studio": "prisma studio",
20
+ "build": "npm run prisma:generate && tsc",
21
+ "npm:publish:dryrun": "npm publish --dry-run",
22
+ "npm:publish": "npm publish --access public"
23
+ },
24
+ "devDependencies": {
25
+ "@types/better-sqlite3": "^7.6.13",
26
+ "typescript": "^5.9.3"
27
+ },
28
+ "dependencies": {
29
+ "@naisys/common": "3.0.0-beta.10",
30
+ "@naisys/common-node": "3.0.0-beta.10",
31
+ "@prisma/adapter-better-sqlite3": "^7.5.0",
32
+ "@prisma/client": "^7.5.0",
33
+ "better-sqlite3": "^12.6.2",
34
+ "prisma": "^7.5.0"
35
+ },
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "default": "./dist/index.js"
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,237 @@
1
+ -- CreateTable
2
+ CREATE TABLE "context_log" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "user_id" INTEGER NOT NULL,
5
+ "run_id" INTEGER NOT NULL,
6
+ "session_id" INTEGER NOT NULL,
7
+ "host_id" INTEGER NOT NULL,
8
+ "role" TEXT NOT NULL,
9
+ "source" TEXT NOT NULL,
10
+ "type" TEXT NOT NULL,
11
+ "message" TEXT NOT NULL,
12
+ "attachment_id" INTEGER,
13
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
14
+ CONSTRAINT "context_log_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
15
+ CONSTRAINT "context_log_user_id_run_id_session_id_fkey" FOREIGN KEY ("user_id", "run_id", "session_id") REFERENCES "run_session" ("user_id", "run_id", "session_id") ON DELETE NO ACTION ON UPDATE NO ACTION,
16
+ CONSTRAINT "context_log_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
17
+ CONSTRAINT "context_log_attachment_id_fkey" FOREIGN KEY ("attachment_id") REFERENCES "attachments" ("id") ON DELETE SET NULL ON UPDATE NO ACTION
18
+ );
19
+
20
+ -- CreateTable
21
+ CREATE TABLE "costs" (
22
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
23
+ "user_id" INTEGER NOT NULL,
24
+ "run_id" INTEGER NOT NULL,
25
+ "session_id" INTEGER NOT NULL,
26
+ "host_id" INTEGER NOT NULL,
27
+ "source" TEXT NOT NULL,
28
+ "model" TEXT NOT NULL,
29
+ "cost" REAL DEFAULT 0,
30
+ "input_tokens" INTEGER DEFAULT 0,
31
+ "output_tokens" INTEGER DEFAULT 0,
32
+ "cache_write_tokens" INTEGER DEFAULT 0,
33
+ "cache_read_tokens" INTEGER DEFAULT 0,
34
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
35
+ CONSTRAINT "costs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
36
+ CONSTRAINT "costs_user_id_run_id_session_id_fkey" FOREIGN KEY ("user_id", "run_id", "session_id") REFERENCES "run_session" ("user_id", "run_id", "session_id") ON DELETE NO ACTION ON UPDATE NO ACTION,
37
+ CONSTRAINT "costs_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
38
+ );
39
+
40
+ -- CreateTable
41
+ CREATE TABLE "mail_messages" (
42
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
43
+ "from_user_id" INTEGER,
44
+ "host_id" INTEGER,
45
+ "kind" TEXT NOT NULL DEFAULT 'mail',
46
+ "participant_ids" TEXT,
47
+ "subject" TEXT NOT NULL,
48
+ "body" TEXT NOT NULL,
49
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
50
+ CONSTRAINT "mail_messages_from_user_id_fkey" FOREIGN KEY ("from_user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
51
+ CONSTRAINT "mail_messages_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
52
+ );
53
+
54
+ -- CreateTable
55
+ CREATE TABLE "attachments" (
56
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
57
+ "filepath" TEXT NOT NULL,
58
+ "filename" TEXT NOT NULL,
59
+ "file_size" INTEGER NOT NULL,
60
+ "file_hash" TEXT NOT NULL,
61
+ "purpose" TEXT NOT NULL,
62
+ "uploaded_by" INTEGER NOT NULL,
63
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
64
+ CONSTRAINT "attachments_uploaded_by_fkey" FOREIGN KEY ("uploaded_by") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
65
+ );
66
+
67
+ -- CreateTable
68
+ CREATE TABLE "mail_attachments" (
69
+ "message_id" INTEGER NOT NULL,
70
+ "attachment_id" INTEGER NOT NULL,
71
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
72
+
73
+ PRIMARY KEY ("message_id", "attachment_id"),
74
+ CONSTRAINT "mail_attachments_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "mail_messages" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
75
+ CONSTRAINT "mail_attachments_attachment_id_fkey" FOREIGN KEY ("attachment_id") REFERENCES "attachments" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
76
+ );
77
+
78
+ -- CreateTable
79
+ CREATE TABLE "mail_recipients" (
80
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
81
+ "message_id" INTEGER NOT NULL,
82
+ "user_id" INTEGER NOT NULL,
83
+ "type" TEXT NOT NULL,
84
+ "read_at" DATETIME,
85
+ "archived_at" DATETIME,
86
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
87
+ CONSTRAINT "mail_recipients_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "mail_messages" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
88
+ CONSTRAINT "mail_recipients_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
89
+ );
90
+
91
+ -- CreateTable
92
+ CREATE TABLE "users" (
93
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
94
+ "uuid" TEXT NOT NULL,
95
+ "username" TEXT NOT NULL,
96
+ "title" TEXT NOT NULL,
97
+ "api_key" TEXT NOT NULL,
98
+ "lead_user_id" INTEGER,
99
+ "config" TEXT NOT NULL DEFAULT '{}',
100
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
101
+ "updated_at" DATETIME NOT NULL,
102
+ "archived" BOOLEAN NOT NULL DEFAULT false,
103
+ CONSTRAINT "users_lead_user_id_fkey" FOREIGN KEY ("lead_user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
104
+ );
105
+
106
+ -- CreateTable
107
+ CREATE TABLE "user_notifications" (
108
+ "user_id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
109
+ "latest_host_id" INTEGER,
110
+ "latest_log_id" INTEGER NOT NULL DEFAULT 0,
111
+ "latest_mail_id" INTEGER NOT NULL DEFAULT 0,
112
+ "latest_chat_id" INTEGER NOT NULL DEFAULT 0,
113
+ "last_active" DATETIME,
114
+ "updated_at" DATETIME NOT NULL,
115
+ CONSTRAINT "user_notifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
116
+ CONSTRAINT "user_notifications_latest_host_id_fkey" FOREIGN KEY ("latest_host_id") REFERENCES "hosts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
117
+ );
118
+
119
+ -- CreateTable
120
+ CREATE TABLE "user_hosts" (
121
+ "user_id" INTEGER NOT NULL,
122
+ "host_id" INTEGER NOT NULL,
123
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
124
+
125
+ PRIMARY KEY ("user_id", "host_id"),
126
+ CONSTRAINT "user_hosts_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
127
+ CONSTRAINT "user_hosts_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
128
+ );
129
+
130
+ -- CreateTable
131
+ CREATE TABLE "run_session" (
132
+ "user_id" INTEGER NOT NULL,
133
+ "run_id" INTEGER NOT NULL,
134
+ "session_id" INTEGER NOT NULL,
135
+ "host_id" INTEGER NOT NULL,
136
+ "last_active" DATETIME NOT NULL,
137
+ "model_name" TEXT NOT NULL,
138
+ "latest_log_id" INTEGER NOT NULL DEFAULT 0,
139
+ "total_lines" INTEGER NOT NULL DEFAULT 0,
140
+ "total_cost" REAL NOT NULL DEFAULT 0,
141
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
142
+ "updated_at" DATETIME NOT NULL,
143
+
144
+ PRIMARY KEY ("user_id", "run_id", "session_id"),
145
+ CONSTRAINT "run_session_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
146
+ CONSTRAINT "run_session_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
147
+ );
148
+
149
+ -- CreateTable
150
+ CREATE TABLE "schema_version" (
151
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT DEFAULT 1,
152
+ "version" INTEGER NOT NULL,
153
+ "updated" DATETIME NOT NULL
154
+ );
155
+
156
+ -- CreateTable
157
+ CREATE TABLE "hosts" (
158
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
159
+ "name" TEXT NOT NULL,
160
+ "last_active" DATETIME,
161
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
162
+ );
163
+
164
+ -- CreateTable
165
+ CREATE TABLE "variables" (
166
+ "key" TEXT NOT NULL PRIMARY KEY,
167
+ "value" TEXT NOT NULL,
168
+ "created_by" TEXT NOT NULL,
169
+ "updated_by" TEXT NOT NULL,
170
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
171
+ "updated_at" DATETIME NOT NULL
172
+ );
173
+
174
+ -- CreateTable
175
+ CREATE TABLE "models" (
176
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
177
+ "key" TEXT NOT NULL,
178
+ "type" TEXT NOT NULL,
179
+ "label" TEXT NOT NULL,
180
+ "version_name" TEXT NOT NULL,
181
+ "is_builtin" BOOLEAN NOT NULL DEFAULT false,
182
+ "is_custom" BOOLEAN NOT NULL DEFAULT false,
183
+ "meta" TEXT NOT NULL,
184
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
185
+ "updated_at" DATETIME NOT NULL
186
+ );
187
+
188
+ -- CreateIndex
189
+ CREATE INDEX "idx_context_log_user_run_session" ON "context_log"("user_id", "run_id", "session_id");
190
+
191
+ -- CreateIndex
192
+ CREATE INDEX "idx_costs_created_at" ON "costs"("created_at");
193
+
194
+ -- CreateIndex
195
+ CREATE INDEX "idx_costs_user_id" ON "costs"("user_id");
196
+
197
+ -- CreateIndex
198
+ CREATE INDEX "idx_costs_user_run_session_source_model" ON "costs"("user_id", "run_id", "session_id", "source", "model");
199
+
200
+ -- CreateIndex
201
+ CREATE INDEX "idx_mail_messages_from_user_id" ON "mail_messages"("from_user_id");
202
+
203
+ -- CreateIndex
204
+ CREATE INDEX "idx_mail_messages_created_at_desc" ON "mail_messages"("created_at" DESC);
205
+
206
+ -- CreateIndex
207
+ CREATE INDEX "idx_mail_messages_participant_ids" ON "mail_messages"("participant_ids");
208
+
209
+ -- CreateIndex
210
+ CREATE INDEX "idx_mail_recipients_message_user" ON "mail_recipients"("message_id", "user_id");
211
+
212
+ -- CreateIndex
213
+ CREATE INDEX "idx_mail_recipients_user_message_desc" ON "mail_recipients"("user_id", "message_id" DESC);
214
+
215
+ -- CreateIndex
216
+ CREATE UNIQUE INDEX "users_uuid_key" ON "users"("uuid");
217
+
218
+ -- CreateIndex
219
+ CREATE UNIQUE INDEX "users_api_key_key" ON "users"("api_key");
220
+
221
+ -- CreateIndex
222
+ CREATE INDEX "idx_users_username" ON "users"("username");
223
+
224
+ -- CreateIndex
225
+ CREATE INDEX "idx_run_session_run_id_desc" ON "run_session"("run_id" DESC);
226
+
227
+ -- CreateIndex
228
+ CREATE INDEX "idx_run_session_user_last_active_desc" ON "run_session"("user_id", "last_active" DESC);
229
+
230
+ -- CreateIndex
231
+ CREATE UNIQUE INDEX "unq_schema_version_version" ON "schema_version"("version");
232
+
233
+ -- CreateIndex
234
+ CREATE UNIQUE INDEX "unq_hosts_name" ON "hosts"("name");
235
+
236
+ -- CreateIndex
237
+ CREATE UNIQUE INDEX "models_key_key" ON "models"("key");
@@ -0,0 +1,20 @@
1
+ -- RedefineTables
2
+ PRAGMA defer_foreign_keys=ON;
3
+ PRAGMA foreign_keys=OFF;
4
+ CREATE TABLE "new_hosts" (
5
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
6
+ "name" TEXT NOT NULL,
7
+ "restricted" BOOLEAN NOT NULL DEFAULT false,
8
+ "last_active" DATETIME,
9
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
10
+ );
11
+ INSERT INTO "new_hosts" ("created_at", "id", "last_active", "name") SELECT "created_at", "id", "last_active", "name" FROM "hosts";
12
+ DROP TABLE "hosts";
13
+ ALTER TABLE "new_hosts" RENAME TO "hosts";
14
+ CREATE UNIQUE INDEX "unq_hosts_name" ON "hosts"("name");
15
+ PRAGMA foreign_keys=ON;
16
+ PRAGMA defer_foreign_keys=OFF;
17
+
18
+ -- Update schema version
19
+ INSERT INTO "schema_version" ("id", "version", "updated") VALUES (1, 20, datetime('now'))
20
+ ON CONFLICT ("id") DO UPDATE SET "version" = 20, "updated" = datetime('now');
@@ -0,0 +1,21 @@
1
+ -- RedefineTables
2
+ PRAGMA defer_foreign_keys=ON;
3
+ PRAGMA foreign_keys=OFF;
4
+ CREATE TABLE "new_hosts" (
5
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
6
+ "name" TEXT NOT NULL,
7
+ "restricted" BOOLEAN NOT NULL DEFAULT false,
8
+ "host_type" TEXT NOT NULL DEFAULT 'naisys',
9
+ "last_active" DATETIME,
10
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
11
+ );
12
+ INSERT INTO "new_hosts" ("created_at", "id", "last_active", "name", "restricted") SELECT "created_at", "id", "last_active", "name", "restricted" FROM "hosts";
13
+ DROP TABLE "hosts";
14
+ ALTER TABLE "new_hosts" RENAME TO "hosts";
15
+ CREATE UNIQUE INDEX "unq_hosts_name" ON "hosts"("name");
16
+ PRAGMA foreign_keys=ON;
17
+ PRAGMA defer_foreign_keys=OFF;
18
+
19
+ -- Update schema version
20
+ INSERT INTO "schema_version" ("id", "version", "updated") VALUES (1, 21, datetime('now'))
21
+ ON CONFLICT ("id") DO UPDATE SET "version" = 21, "updated" = datetime('now');
@@ -0,0 +1,37 @@
1
+ -- Delete orphaned mail data for messages with NULL from_user_id
2
+ DELETE FROM "mail_attachments" WHERE "message_id" IN (
3
+ SELECT "id" FROM "mail_messages" WHERE "from_user_id" IS NULL
4
+ );
5
+ DELETE FROM "mail_recipients" WHERE "message_id" IN (
6
+ SELECT "id" FROM "mail_messages" WHERE "from_user_id" IS NULL
7
+ );
8
+ DELETE FROM "mail_messages" WHERE "from_user_id" IS NULL;
9
+
10
+ -- RedefineTables
11
+ PRAGMA defer_foreign_keys=ON;
12
+ PRAGMA foreign_keys=OFF;
13
+ CREATE TABLE "new_mail_messages" (
14
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
15
+ "from_user_id" INTEGER NOT NULL,
16
+ "host_id" INTEGER,
17
+ "kind" TEXT NOT NULL DEFAULT 'mail',
18
+ "participant_ids" TEXT,
19
+ "subject" TEXT NOT NULL,
20
+ "body" TEXT NOT NULL,
21
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
22
+ CONSTRAINT "mail_messages_from_user_id_fkey" FOREIGN KEY ("from_user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
23
+ CONSTRAINT "mail_messages_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
24
+ );
25
+ INSERT INTO "new_mail_messages" ("id", "from_user_id", "host_id", "kind", "participant_ids", "subject", "body", "created_at")
26
+ SELECT "id", "from_user_id", "host_id", "kind", "participant_ids", "subject", "body", "created_at" FROM "mail_messages";
27
+ DROP TABLE "mail_messages";
28
+ ALTER TABLE "new_mail_messages" RENAME TO "mail_messages";
29
+ CREATE INDEX "idx_mail_messages_from_user_id" ON "mail_messages"("from_user_id");
30
+ CREATE INDEX "idx_mail_messages_created_at_desc" ON "mail_messages"("created_at" DESC);
31
+ CREATE INDEX "idx_mail_messages_participant_ids" ON "mail_messages"("participant_ids");
32
+ PRAGMA foreign_keys=ON;
33
+ PRAGMA defer_foreign_keys=OFF;
34
+
35
+ -- Update schema version
36
+ INSERT INTO "schema_version" ("id", "version", "updated") VALUES (1, 22, datetime('now'))
37
+ ON CONFLICT ("id") DO UPDATE SET "version" = 22, "updated" = datetime('now');
@@ -0,0 +1,2 @@
1
+ -- AlterTable
2
+ ALTER TABLE "user_notifications" ADD COLUMN "cost_suspended_reason" TEXT;
@@ -0,0 +1,31 @@
1
+ -- Backfill any NULL participant_ids (shouldn't exist, but just in case)
2
+ UPDATE "mail_messages" SET "participant_ids" = '' WHERE "participant_ids" IS NULL;
3
+
4
+ -- RedefineTables
5
+ PRAGMA defer_foreign_keys=ON;
6
+ PRAGMA foreign_keys=OFF;
7
+ CREATE TABLE "new_mail_messages" (
8
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
9
+ "from_user_id" INTEGER NOT NULL,
10
+ "host_id" INTEGER,
11
+ "kind" TEXT NOT NULL DEFAULT 'mail',
12
+ "participant_ids" TEXT NOT NULL,
13
+ "subject" TEXT NOT NULL,
14
+ "body" TEXT NOT NULL,
15
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
16
+ CONSTRAINT "mail_messages_from_user_id_fkey" FOREIGN KEY ("from_user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
17
+ CONSTRAINT "mail_messages_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
18
+ );
19
+ INSERT INTO "new_mail_messages" ("id", "from_user_id", "host_id", "kind", "participant_ids", "subject", "body", "created_at")
20
+ SELECT "id", "from_user_id", "host_id", "kind", "participant_ids", "subject", "body", "created_at" FROM "mail_messages";
21
+ DROP TABLE "mail_messages";
22
+ ALTER TABLE "new_mail_messages" RENAME TO "mail_messages";
23
+ CREATE INDEX "idx_mail_messages_from_user_id" ON "mail_messages"("from_user_id");
24
+ CREATE INDEX "idx_mail_messages_created_at_desc" ON "mail_messages"("created_at" DESC);
25
+ CREATE INDEX "idx_mail_messages_participant_ids" ON "mail_messages"("participant_ids");
26
+ PRAGMA foreign_keys=ON;
27
+ PRAGMA defer_foreign_keys=OFF;
28
+
29
+ -- Update schema version
30
+ INSERT INTO "schema_version" ("id", "version", "updated") VALUES (1, 24, datetime('now'))
31
+ ON CONFLICT ("id") DO UPDATE SET "version" = 24, "updated" = datetime('now');
@@ -0,0 +1 @@
1
+ ALTER TABLE "hosts" ADD COLUMN "last_ip" TEXT;
@@ -0,0 +1,61 @@
1
+ -- Step 1: Add participants column
2
+ ALTER TABLE "mail_messages" ADD COLUMN "participants" TEXT NOT NULL DEFAULT '';
3
+
4
+ -- Step 2: Backfill using recursive CTE (split participant_ids CSV into rows,
5
+ -- join to users for usernames, GROUP_CONCAT back into sorted CSV)
6
+ WITH RECURSIVE split(msg_id, pid_str, participant_id, rest) AS (
7
+ SELECT id, participant_ids,
8
+ CAST(CASE WHEN INSTR(participant_ids, ',') > 0
9
+ THEN SUBSTR(participant_ids, 1, INSTR(participant_ids, ',') - 1)
10
+ ELSE participant_ids END AS INTEGER),
11
+ CASE WHEN INSTR(participant_ids, ',') > 0
12
+ THEN SUBSTR(participant_ids, INSTR(participant_ids, ',') + 1)
13
+ ELSE '' END
14
+ FROM mail_messages WHERE participant_ids != ''
15
+ UNION ALL
16
+ SELECT msg_id, pid_str,
17
+ CAST(CASE WHEN INSTR(rest, ',') > 0
18
+ THEN SUBSTR(rest, 1, INSTR(rest, ',') - 1)
19
+ ELSE rest END AS INTEGER),
20
+ CASE WHEN INSTR(rest, ',') > 0
21
+ THEN SUBSTR(rest, INSTR(rest, ',') + 1)
22
+ ELSE '' END
23
+ FROM split WHERE rest != ''
24
+ ),
25
+ resolved AS (
26
+ SELECT s.msg_id, s.pid_str, u.username
27
+ FROM split s JOIN users u ON u.id = s.participant_id
28
+ ),
29
+ grouped AS (
30
+ SELECT pid_str, GROUP_CONCAT(username, ',') AS participants
31
+ FROM (SELECT DISTINCT pid_str, username FROM resolved ORDER BY pid_str, username)
32
+ GROUP BY pid_str
33
+ )
34
+ UPDATE mail_messages SET participants = (
35
+ SELECT g.participants FROM grouped g WHERE g.pid_str = mail_messages.participant_ids
36
+ ) WHERE participant_ids != '';
37
+
38
+ -- Step 3: Redefine table without participant_ids
39
+ PRAGMA defer_foreign_keys=ON;
40
+ PRAGMA foreign_keys=OFF;
41
+ CREATE TABLE "new_mail_messages" (
42
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
43
+ "from_user_id" INTEGER NOT NULL,
44
+ "host_id" INTEGER,
45
+ "kind" TEXT NOT NULL DEFAULT 'mail',
46
+ "participants" TEXT NOT NULL DEFAULT '',
47
+ "subject" TEXT NOT NULL,
48
+ "body" TEXT NOT NULL,
49
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
50
+ CONSTRAINT "mail_messages_from_user_id_fkey" FOREIGN KEY ("from_user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
51
+ CONSTRAINT "mail_messages_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE SET NULL ON UPDATE CASCADE
52
+ );
53
+ INSERT INTO "new_mail_messages" ("id", "from_user_id", "host_id", "kind", "participants", "subject", "body", "created_at")
54
+ SELECT "id", "from_user_id", "host_id", "kind", "participants", "subject", "body", "created_at" FROM "mail_messages";
55
+ DROP TABLE "mail_messages";
56
+ ALTER TABLE "new_mail_messages" RENAME TO "mail_messages";
57
+ CREATE INDEX "idx_mail_messages_from_user_id" ON "mail_messages"("from_user_id");
58
+ CREATE INDEX "idx_mail_messages_created_at_desc" ON "mail_messages"("created_at" DESC);
59
+ CREATE INDEX "idx_mail_messages_participants" ON "mail_messages"("participants");
60
+ PRAGMA foreign_keys=ON;
61
+ PRAGMA defer_foreign_keys=OFF;
@@ -0,0 +1,31 @@
1
+ -- RedefineTables
2
+ PRAGMA defer_foreign_keys=ON;
3
+ PRAGMA foreign_keys=OFF;
4
+ CREATE TABLE "new_context_log" (
5
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
6
+ "user_id" INTEGER NOT NULL,
7
+ "run_id" INTEGER NOT NULL,
8
+ "session_id" INTEGER NOT NULL,
9
+ "host_id" INTEGER NOT NULL,
10
+ "role" TEXT NOT NULL,
11
+ "source" TEXT,
12
+ "type" TEXT,
13
+ "message" TEXT NOT NULL,
14
+ "attachment_id" INTEGER,
15
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
16
+ CONSTRAINT "context_log_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION,
17
+ CONSTRAINT "context_log_user_id_run_id_session_id_fkey" FOREIGN KEY ("user_id", "run_id", "session_id") REFERENCES "run_session" ("user_id", "run_id", "session_id") ON DELETE NO ACTION ON UPDATE NO ACTION,
18
+ CONSTRAINT "context_log_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
19
+ CONSTRAINT "context_log_attachment_id_fkey" FOREIGN KEY ("attachment_id") REFERENCES "attachments" ("id") ON DELETE SET NULL ON UPDATE NO ACTION
20
+ );
21
+ INSERT INTO "new_context_log" ("id", "user_id", "run_id", "session_id", "host_id", "role", "source", "type", "message", "attachment_id", "created_at")
22
+ SELECT "id", "user_id", "run_id", "session_id", "host_id", "role",
23
+ NULLIF("source", ''),
24
+ NULLIF("type", ''),
25
+ "message", "attachment_id", "created_at"
26
+ FROM "context_log";
27
+ DROP TABLE "context_log";
28
+ ALTER TABLE "new_context_log" RENAME TO "context_log";
29
+ CREATE INDEX "idx_context_log_user_run_session" ON "context_log"("user_id", "run_id", "session_id");
30
+ PRAGMA foreign_keys=ON;
31
+ PRAGMA defer_foreign_keys=OFF;
@@ -0,0 +1,5 @@
1
+ -- AlterTable
2
+ ALTER TABLE "users" ADD COLUMN "enabled" BOOLEAN NOT NULL DEFAULT true;
3
+
4
+ -- Migrate: disable archived users
5
+ UPDATE "users" SET "enabled" = false WHERE "archived" = true;
@@ -0,0 +1,13 @@
1
+ -- CreateTable
2
+ CREATE TABLE "config_revisions" (
3
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4
+ "user_id" INTEGER NOT NULL,
5
+ "config" TEXT NOT NULL,
6
+ "changed_by_id" INTEGER NOT NULL,
7
+ "created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ CONSTRAINT "config_revisions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE NO ACTION,
9
+ CONSTRAINT "config_revisions_changed_by_id_fkey" FOREIGN KEY ("changed_by_id") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION
10
+ );
11
+
12
+ -- CreateIndex
13
+ CREATE INDEX "idx_config_revisions_user_created_desc" ON "config_revisions"("user_id", "created_at" DESC);
@@ -0,0 +1,13 @@
1
+ -- SQLite has no enum types; Prisma stores RecipientType as text.
2
+ -- No ALTER TYPE needed — 'from' is valid as soon as it's inserted.
3
+
4
+ -- Backfill: add 'from' recipient records for all existing messages
5
+ -- where the sender doesn't already have a recipient record.
6
+ -- Sets read_at = created_at since the sender has already "read" their own message.
7
+ INSERT INTO mail_recipients (message_id, user_id, type, read_at, created_at)
8
+ SELECT m.id, m.from_user_id, 'from', m.created_at, m.created_at
9
+ FROM mail_messages m
10
+ WHERE NOT EXISTS (
11
+ SELECT 1 FROM mail_recipients r
12
+ WHERE r.message_id = m.id AND r.user_id = m.from_user_id
13
+ );
@@ -0,0 +1 @@
1
+ ALTER TABLE "user_notifications" ADD COLUMN "spend_limit_reset_at" DATETIME;
@@ -0,0 +1 @@
1
+ ALTER TABLE "user_notifications" ADD COLUMN "budget_left" DECIMAL;
@@ -0,0 +1,2 @@
1
+ -- Rename context_log type enum value: workspace -> tool
2
+ UPDATE "context_log" SET "type" = 'tool' WHERE "type" = 'workspace';
@@ -0,0 +1,2 @@
1
+ -- Add export_to_shell flag to variables (default false)
2
+ ALTER TABLE "variables" ADD COLUMN "export_to_shell" BOOLEAN NOT NULL DEFAULT 0;
@@ -0,0 +1,8 @@
1
+ -- Add public_id column to attachments (nullable initially for migration)
2
+ ALTER TABLE "attachments" ADD COLUMN "public_id" TEXT;
3
+
4
+ -- Populate existing rows with 12-char random hex
5
+ UPDATE "attachments" SET "public_id" = lower(hex(randomblob(6)));
6
+
7
+ -- Make non-nullable and add unique index
8
+ CREATE UNIQUE INDEX "attachments_public_id_key" ON "attachments"("public_id");
@@ -0,0 +1,3 @@
1
+ # Please do not edit this file manually
2
+ # It should be added in your version-control system (e.g., Git)
3
+ provider = "sqlite"