@sixb/sqlite 0.1.11 → 0.1.13
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.
- package/README.md +32 -0
- package/dist/agents/rows.d.ts +2 -0
- package/dist/agents/rows.d.ts.map +1 -1
- package/dist/agents/threads.d.ts +2 -1
- package/dist/agents/threads.d.ts.map +1 -1
- package/dist/auth-storage/access-tokens.d.ts +0 -1
- package/dist/auth-storage/access-tokens.d.ts.map +1 -1
- package/dist/auth-storage/auth-storage.d.ts.map +1 -1
- package/dist/auth-storage/device-authorizations.d.ts.map +1 -1
- package/dist/auth-storage/rows.d.ts +6 -3
- package/dist/auth-storage/rows.d.ts.map +1 -1
- package/dist/auth-storage/sessions.d.ts +8 -1
- package/dist/auth-storage/sessions.d.ts.map +1 -1
- package/dist/auth-storage/shared.d.ts.map +1 -1
- package/dist/file-upload-session-storage.d.ts +6 -0
- package/dist/file-upload-session-storage.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1062 -137
- package/dist/index.js.map +28 -23
- package/dist/migrations.d.ts.map +1 -1
- package/dist/objects/query-compiler.d.ts +9 -2
- package/dist/objects/query-compiler.d.ts.map +1 -1
- package/dist/objects/read-scope.d.ts +3 -0
- package/dist/objects/read-scope.d.ts.map +1 -1
- package/dist/objects/reader.d.ts +1 -0
- package/dist/objects/reader.d.ts.map +1 -1
- package/dist/objects/rows.d.ts +1 -0
- package/dist/objects/rows.d.ts.map +1 -1
- package/dist/objects/storage.d.ts.map +1 -1
- package/dist/objects/vector-extension.d.ts +4 -0
- package/dist/objects/vector-extension.d.ts.map +1 -0
- package/dist/ontology-storage/index.d.ts +4 -0
- package/dist/ontology-storage/index.d.ts.map +1 -1
- package/dist/ontology-storage/materializations.d.ts +1 -2
- package/dist/ontology-storage/materializations.d.ts.map +1 -1
- package/dist/ontology-storage/shared.d.ts +2 -1
- package/dist/ontology-storage/shared.d.ts.map +1 -1
- package/dist/ontology-storage/vector-indexing.d.ts +20 -0
- package/dist/ontology-storage/vector-indexing.d.ts.map +1 -0
- package/dist/ontology-storage/vectors.d.ts +16 -0
- package/dist/ontology-storage/vectors.d.ts.map +1 -0
- package/dist/vector-encoding.d.ts +3 -0
- package/dist/vector-encoding.d.ts.map +1 -0
- package/dist/workflow-intervention-storage.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/agents/rows.ts +12 -0
- package/src/agents/threads.ts +41 -2
- package/src/ai-cost-storage.ts +4 -0
- package/src/auth-storage/access-tokens.ts +11 -18
- package/src/auth-storage/auth-storage.ts +5 -7
- package/src/auth-storage/device-authorizations.ts +3 -5
- package/src/auth-storage/rows.ts +17 -7
- package/src/auth-storage/sessions.ts +89 -4
- package/src/auth-storage/shared.ts +9 -3
- package/src/file-upload-session-storage.ts +136 -0
- package/src/index.ts +5 -0
- package/src/migrations/041-agent-thread-workspaces.sql +2 -0
- package/src/migrations/042-vector-profiles.sql +21 -0
- package/src/migrations/043-vector-indexing.sql +197 -0
- package/src/migrations/044-vector-batching.sql +3 -0
- package/src/migrations/045-agent-thread-sandbox-state.sql +2 -0
- package/src/migrations/046-workflow-intervention-principals.sql +15 -0
- package/src/migrations/047-ontology-commit-attribution.sql +67 -0
- package/src/migrations/048-file-upload-sessions.sql +35 -0
- package/src/migrations/049-native-sessions.sql +12 -0
- package/src/migrations.ts +26 -0
- package/src/objects/query-compiler.ts +164 -11
- package/src/objects/read-scope.ts +34 -31
- package/src/objects/reader.ts +29 -1
- package/src/objects/rows.ts +5 -1
- package/src/objects/storage.ts +13 -1
- package/src/objects/vector-extension.ts +26 -0
- package/src/ontology-storage/index.ts +16 -0
- package/src/ontology-storage/materializations.ts +29 -59
- package/src/ontology-storage/shared.ts +7 -3
- package/src/ontology-storage/vector-indexing.ts +196 -0
- package/src/ontology-storage/vectors.ts +182 -0
- package/src/vector-encoding.ts +9 -0
- package/src/workflow-intervention-storage.ts +9 -9
package/src/auth-storage/rows.ts
CHANGED
|
@@ -73,6 +73,11 @@ export interface SqliteAuthSessionRow {
|
|
|
73
73
|
readonly last_seen_at: string | null
|
|
74
74
|
readonly user_agent: string | null
|
|
75
75
|
readonly ip_address: string | null
|
|
76
|
+
readonly client_name: string | null
|
|
77
|
+
readonly access_expires_at: string | null
|
|
78
|
+
readonly refresh_token_hash: string | null
|
|
79
|
+
readonly previous_refresh_token_hash: string | null
|
|
80
|
+
readonly refreshed_at: string | null
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
export interface SqliteAuthAccessTokenRow {
|
|
@@ -80,7 +85,7 @@ export interface SqliteAuthAccessTokenRow {
|
|
|
80
85
|
readonly id: string
|
|
81
86
|
readonly name: string
|
|
82
87
|
readonly kind: AccessTokenRecord["kind"]
|
|
83
|
-
readonly subject_type: AccessTokenRecord["
|
|
88
|
+
readonly subject_type: AccessTokenRecord["subject"]["type"]
|
|
84
89
|
readonly subject_id: string
|
|
85
90
|
readonly token_hash: string
|
|
86
91
|
readonly group_ids: string | null
|
|
@@ -152,8 +157,6 @@ export interface SqliteAuthDeviceAuthorizationRow {
|
|
|
152
157
|
readonly device_code_hash: string
|
|
153
158
|
readonly user_code: string
|
|
154
159
|
readonly client_name: string
|
|
155
|
-
readonly token_name: string
|
|
156
|
-
readonly token_expires_at: string
|
|
157
160
|
readonly status: DeviceAuthorizationRecord["status"]
|
|
158
161
|
readonly approved_user_id: string | null
|
|
159
162
|
readonly approved_session_id: string | null
|
|
@@ -236,6 +239,16 @@ export function rowToSessionRecord(row: SqliteAuthSessionRow): SessionRecord {
|
|
|
236
239
|
lastSeenAt: row.last_seen_at ? new Date(row.last_seen_at) : undefined,
|
|
237
240
|
userAgent: row.user_agent ?? undefined,
|
|
238
241
|
ipAddress: row.ip_address ?? undefined,
|
|
242
|
+
bearer:
|
|
243
|
+
row.refresh_token_hash !== null && row.client_name !== null && row.access_expires_at !== null
|
|
244
|
+
? {
|
|
245
|
+
clientName: row.client_name,
|
|
246
|
+
accessExpiresAt: new Date(row.access_expires_at),
|
|
247
|
+
refreshTokenHash: row.refresh_token_hash,
|
|
248
|
+
previousRefreshTokenHash: row.previous_refresh_token_hash ?? undefined,
|
|
249
|
+
refreshedAt: row.refreshed_at ? new Date(row.refreshed_at) : undefined,
|
|
250
|
+
}
|
|
251
|
+
: undefined,
|
|
239
252
|
}
|
|
240
253
|
}
|
|
241
254
|
|
|
@@ -245,8 +258,7 @@ export function rowToAccessTokenRecord(row: SqliteAuthAccessTokenRow): AccessTok
|
|
|
245
258
|
projectId: row.project_id,
|
|
246
259
|
name: row.name,
|
|
247
260
|
kind: row.kind,
|
|
248
|
-
|
|
249
|
-
subjectId: row.subject_id,
|
|
261
|
+
subject: { type: row.subject_type, id: row.subject_id },
|
|
250
262
|
tokenHash: row.token_hash,
|
|
251
263
|
groupIds: parseOptionalStringArray(row.group_ids),
|
|
252
264
|
createdByPrincipal:
|
|
@@ -347,8 +359,6 @@ export function rowToDeviceAuthorizationRecord(
|
|
|
347
359
|
deviceCodeHash: row.device_code_hash,
|
|
348
360
|
userCode: row.user_code,
|
|
349
361
|
clientName: row.client_name,
|
|
350
|
-
tokenName: row.token_name,
|
|
351
|
-
tokenExpiresAt: new Date(row.token_expires_at),
|
|
352
362
|
status: row.status,
|
|
353
363
|
approvedUserId: row.approved_user_id ?? undefined,
|
|
354
364
|
approvedSessionId: row.approved_session_id ?? undefined,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite"
|
|
2
2
|
import type { AuthSessionAudience } from "@sixb/core"
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
AuthSessionStore,
|
|
5
|
+
CreateAuthSessionInput,
|
|
6
|
+
RotateSessionRefreshTokenResult,
|
|
7
|
+
SessionRecord,
|
|
8
|
+
} from "@sixb/core/storage"
|
|
4
9
|
import { AuthStorageError } from "@sixb/core/storage"
|
|
5
10
|
import { runImmediateTransaction } from "../transactions"
|
|
6
11
|
import type { SqliteAuthSessionRow } from "./rows"
|
|
@@ -99,11 +104,10 @@ export class SqliteAuthSessionStore implements AuthSessionStore {
|
|
|
99
104
|
|
|
100
105
|
if (
|
|
101
106
|
!row ||
|
|
107
|
+
row.refresh_token_hash !== null ||
|
|
102
108
|
row.audience !== params.audience ||
|
|
103
109
|
row.token_hash !== params.tokenHash ||
|
|
104
|
-
row.
|
|
105
|
-
new Date(row.expires_at) <= params.now ||
|
|
106
|
-
(row.absolute_expires_at !== null && new Date(row.absolute_expires_at) <= params.now)
|
|
110
|
+
!isLiveSessionRow(row, params.now)
|
|
107
111
|
) {
|
|
108
112
|
return null
|
|
109
113
|
}
|
|
@@ -111,6 +115,78 @@ export class SqliteAuthSessionStore implements AuthSessionStore {
|
|
|
111
115
|
return rowToSessionRecord(row)
|
|
112
116
|
}
|
|
113
117
|
|
|
118
|
+
async findValidByAccessTokenHash(params: {
|
|
119
|
+
readonly projectId: string
|
|
120
|
+
readonly id: string
|
|
121
|
+
readonly tokenHash: string
|
|
122
|
+
readonly now: Date
|
|
123
|
+
}): Promise<SessionRecord | null> {
|
|
124
|
+
const row = getSessionRowById(this.db, params)
|
|
125
|
+
if (
|
|
126
|
+
!row ||
|
|
127
|
+
row.access_expires_at === null ||
|
|
128
|
+
row.token_hash !== params.tokenHash ||
|
|
129
|
+
new Date(row.access_expires_at) <= params.now ||
|
|
130
|
+
!isLiveSessionRow(row, params.now)
|
|
131
|
+
) {
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return rowToSessionRecord(row)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async rotateRefreshToken(
|
|
139
|
+
params: Parameters<AuthSessionStore["rotateRefreshToken"]>[0]
|
|
140
|
+
): Promise<RotateSessionRefreshTokenResult> {
|
|
141
|
+
return runImmediateTransaction(this.db, (): RotateSessionRefreshTokenResult => {
|
|
142
|
+
const row = getSessionRowById(this.db, params)
|
|
143
|
+
if (!row?.refresh_token_hash || !isLiveSessionRow(row, params.now)) {
|
|
144
|
+
return { status: "invalid" }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const replacedRecently =
|
|
148
|
+
row.previous_refresh_token_hash === params.refreshTokenHash &&
|
|
149
|
+
row.refreshed_at !== null &&
|
|
150
|
+
params.now.getTime() - new Date(row.refreshed_at).getTime() <= params.reuseGraceMs
|
|
151
|
+
if (row.refresh_token_hash !== params.refreshTokenHash && !replacedRecently) {
|
|
152
|
+
if (row.previous_refresh_token_hash !== params.refreshTokenHash) {
|
|
153
|
+
return { status: "invalid" }
|
|
154
|
+
}
|
|
155
|
+
this.db
|
|
156
|
+
.query("UPDATE auth_sessions SET revoked_at = ? WHERE project_id = ? AND id = ?")
|
|
157
|
+
.run(toIso(params.now), params.projectId, params.id)
|
|
158
|
+
return { status: "reused" }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const expiresAt = toIso(params.next.expiresAt)
|
|
162
|
+
this.db
|
|
163
|
+
.query(`
|
|
164
|
+
UPDATE auth_sessions
|
|
165
|
+
SET token_hash = ?,
|
|
166
|
+
access_expires_at = ?,
|
|
167
|
+
previous_refresh_token_hash = refresh_token_hash,
|
|
168
|
+
refresh_token_hash = ?,
|
|
169
|
+
refreshed_at = ?,
|
|
170
|
+
last_seen_at = ?,
|
|
171
|
+
expires_at = MAX(expires_at, MIN(?, COALESCE(absolute_expires_at, ?)))
|
|
172
|
+
WHERE project_id = ? AND id = ?
|
|
173
|
+
`)
|
|
174
|
+
.run(
|
|
175
|
+
params.next.tokenHash,
|
|
176
|
+
toIso(params.next.accessExpiresAt),
|
|
177
|
+
params.next.refreshTokenHash,
|
|
178
|
+
toIso(params.now),
|
|
179
|
+
toIso(params.now),
|
|
180
|
+
expiresAt,
|
|
181
|
+
expiresAt,
|
|
182
|
+
params.projectId,
|
|
183
|
+
params.id
|
|
184
|
+
)
|
|
185
|
+
const session = getSessionById(this.db, params)
|
|
186
|
+
return session ? { status: "rotated", session } : { status: "invalid" }
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
114
190
|
async renewIfValid(params: {
|
|
115
191
|
readonly projectId: string
|
|
116
192
|
readonly id: string
|
|
@@ -135,6 +211,7 @@ export class SqliteAuthSessionStore implements AuthSessionStore {
|
|
|
135
211
|
AND id = ?
|
|
136
212
|
AND audience = ?
|
|
137
213
|
AND token_hash = ?
|
|
214
|
+
AND refresh_token_hash IS NULL
|
|
138
215
|
AND revoked_at IS NULL
|
|
139
216
|
AND expires_at > ?
|
|
140
217
|
AND (absolute_expires_at IS NULL OR absolute_expires_at > ?)
|
|
@@ -228,3 +305,11 @@ export class SqliteAuthSessionStore implements AuthSessionStore {
|
|
|
228
305
|
})
|
|
229
306
|
}
|
|
230
307
|
}
|
|
308
|
+
|
|
309
|
+
function isLiveSessionRow(row: SqliteAuthSessionRow, now: Date): boolean {
|
|
310
|
+
return (
|
|
311
|
+
row.revoked_at === null &&
|
|
312
|
+
new Date(row.expires_at) > now &&
|
|
313
|
+
(row.absolute_expires_at === null || new Date(row.absolute_expires_at) > now)
|
|
314
|
+
)
|
|
315
|
+
}
|
|
@@ -425,8 +425,11 @@ export function createSession(
|
|
|
425
425
|
expires_at,
|
|
426
426
|
absolute_expires_at,
|
|
427
427
|
user_agent,
|
|
428
|
-
ip_address
|
|
429
|
-
|
|
428
|
+
ip_address,
|
|
429
|
+
client_name,
|
|
430
|
+
access_expires_at,
|
|
431
|
+
refresh_token_hash
|
|
432
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
430
433
|
`
|
|
431
434
|
).run(
|
|
432
435
|
projectId,
|
|
@@ -439,7 +442,10 @@ export function createSession(
|
|
|
439
442
|
toIso(input.expiresAt),
|
|
440
443
|
input.absoluteExpiresAt ? toIso(input.absoluteExpiresAt) : null,
|
|
441
444
|
input.userAgent ?? null,
|
|
442
|
-
input.ipAddress ?? null
|
|
445
|
+
input.ipAddress ?? null,
|
|
446
|
+
input.bearer ? assertNonEmpty(input.bearer.clientName, "Session client name") : null,
|
|
447
|
+
input.bearer ? toIso(input.bearer.accessExpiresAt) : null,
|
|
448
|
+
input.bearer ? assertNonEmpty(input.bearer.refreshTokenHash, "Refresh token hash") : null
|
|
443
449
|
)
|
|
444
450
|
|
|
445
451
|
const session = getSessionById(db, { projectId, id })
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite"
|
|
2
|
+
import {
|
|
3
|
+
DurableFileUploadSessions,
|
|
4
|
+
type FileUploadSessionPersistence,
|
|
5
|
+
type FileUploadSessionPersistenceBackend,
|
|
6
|
+
fileUploadSessionReapAt,
|
|
7
|
+
parseFileUploadSessionRow,
|
|
8
|
+
} from "@sixb/core/internal/file-upload-session-storage-provider"
|
|
9
|
+
import type { FileUploadSession, ListAbandonedFileUploadSessionsInput } from "@sixb/core/storage"
|
|
10
|
+
import { isUniqueConstraintError } from "./storage-errors"
|
|
11
|
+
import { runImmediateTransactionAsync, type SqliteStoreConnection } from "./transactions"
|
|
12
|
+
|
|
13
|
+
export class SqliteFileUploadSessionStorage extends DurableFileUploadSessions {
|
|
14
|
+
constructor(connection: SqliteStoreConnection) {
|
|
15
|
+
super(new SqliteFileUploadSessionBackend(connection.db))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class SqliteFileUploadSessionBackend implements FileUploadSessionPersistenceBackend {
|
|
20
|
+
private readonly persistence: FileUploadSessionPersistence
|
|
21
|
+
|
|
22
|
+
constructor(private readonly db: Database) {
|
|
23
|
+
this.persistence = new SqliteFileUploadSessionPersistence(db)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
read<T>(run: (persistence: FileUploadSessionPersistence) => Promise<T>): Promise<T> {
|
|
27
|
+
return run(this.persistence)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
transaction<T>(run: (persistence: FileUploadSessionPersistence) => Promise<T>): Promise<T> {
|
|
31
|
+
return runImmediateTransactionAsync(this.db, () => run(this.persistence))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class SqliteFileUploadSessionPersistence implements FileUploadSessionPersistence {
|
|
36
|
+
constructor(private readonly db: Database) {}
|
|
37
|
+
|
|
38
|
+
async now(): Promise<Date> {
|
|
39
|
+
const row = this.db.query("SELECT strftime('%Y-%m-%dT%H:%M:%fZ', 'now') AS now").get() as {
|
|
40
|
+
readonly now: string
|
|
41
|
+
}
|
|
42
|
+
return new Date(row.now)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async insert(session: FileUploadSession): Promise<boolean> {
|
|
46
|
+
try {
|
|
47
|
+
this.db
|
|
48
|
+
.query(
|
|
49
|
+
`INSERT INTO file_upload_sessions (
|
|
50
|
+
id, project_id, owner_principal_type, owner_principal_id, strategy, status, file_name,
|
|
51
|
+
media_type, logical_path, expected_size_bytes, expected_digest, provider_upload, signed_parts, file_ref,
|
|
52
|
+
created_at, expires_at, completed_at, aborted_at, reap_at
|
|
53
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
54
|
+
)
|
|
55
|
+
.run(
|
|
56
|
+
session.id,
|
|
57
|
+
session.projectId,
|
|
58
|
+
session.principal.type,
|
|
59
|
+
session.principal.id,
|
|
60
|
+
session.strategy,
|
|
61
|
+
session.status,
|
|
62
|
+
session.fileName ?? null,
|
|
63
|
+
session.mediaType ?? null,
|
|
64
|
+
session.logicalPath ?? null,
|
|
65
|
+
session.expectedSizeBytes ?? null,
|
|
66
|
+
session.expectedDigest ?? null,
|
|
67
|
+
...mutableColumns(session)
|
|
68
|
+
)
|
|
69
|
+
return true
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (isUniqueConstraintError(error)) return false
|
|
72
|
+
throw error
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async get(uploadId: string): Promise<FileUploadSession | null> {
|
|
77
|
+
const row = this.db.query(`${SELECT_SESSION} WHERE id = ?`).get(uploadId)
|
|
78
|
+
return row ? parseFileUploadSessionRow(row as Record<string, unknown>) : null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async update(session: FileUploadSession): Promise<void> {
|
|
82
|
+
this.db
|
|
83
|
+
.query(
|
|
84
|
+
`UPDATE file_upload_sessions
|
|
85
|
+
SET status = ?, provider_upload = ?, signed_parts = ?, file_ref = ?, created_at = ?,
|
|
86
|
+
expires_at = ?, completed_at = ?, aborted_at = ?, reap_at = ?
|
|
87
|
+
WHERE id = ?`
|
|
88
|
+
)
|
|
89
|
+
.run(session.status, ...mutableColumns(session), session.id)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async listAbandoned(
|
|
93
|
+
input: ListAbandonedFileUploadSessionsInput
|
|
94
|
+
): Promise<readonly FileUploadSession[]> {
|
|
95
|
+
const rows = this.db
|
|
96
|
+
.query(
|
|
97
|
+
`${SELECT_SESSION}
|
|
98
|
+
WHERE project_id = ? AND status = 'pending' AND provider_upload IS NOT NULL
|
|
99
|
+
AND expires_at <= ?
|
|
100
|
+
ORDER BY expires_at, id
|
|
101
|
+
LIMIT ?`
|
|
102
|
+
)
|
|
103
|
+
.all(input.projectId, input.now.toISOString(), input.limit)
|
|
104
|
+
return rows.map((row) => parseFileUploadSessionRow(row as Record<string, unknown>))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async deleteReapable(now: Date): Promise<number> {
|
|
108
|
+
return this.db
|
|
109
|
+
.query("DELETE FROM file_upload_sessions WHERE reap_at IS NOT NULL AND reap_at <= ?")
|
|
110
|
+
.run(now.toISOString()).changes
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const SELECT_SESSION = `
|
|
115
|
+
SELECT id, project_id AS projectId, owner_principal_type AS ownerPrincipalType,
|
|
116
|
+
owner_principal_id AS ownerPrincipalId, strategy, status,
|
|
117
|
+
file_name AS fileName, media_type AS mediaType, logical_path AS logicalPath,
|
|
118
|
+
expected_size_bytes AS expectedSizeBytes, expected_digest AS expectedDigest,
|
|
119
|
+
provider_upload AS providerUpload, signed_parts AS signedParts, file_ref AS fileRef,
|
|
120
|
+
created_at AS createdAt, expires_at AS expiresAt, completed_at AS completedAt,
|
|
121
|
+
aborted_at AS abortedAt
|
|
122
|
+
FROM file_upload_sessions`
|
|
123
|
+
|
|
124
|
+
/** Columns a transition may change, in insert/update order after `expected_digest`/`status`. */
|
|
125
|
+
function mutableColumns(session: FileUploadSession) {
|
|
126
|
+
return [
|
|
127
|
+
session.providerUpload === undefined ? null : JSON.stringify(session.providerUpload),
|
|
128
|
+
JSON.stringify(session.signedParts),
|
|
129
|
+
session.fileRef === undefined ? null : JSON.stringify(session.fileRef),
|
|
130
|
+
session.createdAt.toISOString(),
|
|
131
|
+
session.expiresAt.toISOString(),
|
|
132
|
+
session.completedAt?.toISOString() ?? null,
|
|
133
|
+
session.abortedAt?.toISOString() ?? null,
|
|
134
|
+
fileUploadSessionReapAt(session)?.toISOString() ?? null,
|
|
135
|
+
] as const
|
|
136
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { SqliteAiUsageStorage } from "./ai-usage-storage"
|
|
|
39
39
|
import { SqliteAuthStorage } from "./auth-storage"
|
|
40
40
|
import { SqliteConnectorConnectionStorage } from "./connector-connection-storage"
|
|
41
41
|
import { SqliteExecutionStorage } from "./execution-storage"
|
|
42
|
+
import { SqliteFileUploadSessionStorage } from "./file-upload-session-storage"
|
|
42
43
|
import {
|
|
43
44
|
createSqliteStorageMigrators,
|
|
44
45
|
installFreshSqliteSchema,
|
|
@@ -106,6 +107,7 @@ export class SqliteStorage implements MigrationCapableStorage {
|
|
|
106
107
|
readonly shareGrants: ShareGrantStorage
|
|
107
108
|
readonly shareSessions: ShareSessionStorage
|
|
108
109
|
readonly connectorConnections: SqliteConnectorConnectionStorage
|
|
110
|
+
readonly fileUploadSessions: SqliteFileUploadSessionStorage
|
|
109
111
|
readonly migrators: readonly StorageMigrator[]
|
|
110
112
|
|
|
111
113
|
private readonly connection: SqliteStoreConnection
|
|
@@ -172,6 +174,7 @@ export class SqliteStorage implements MigrationCapableStorage {
|
|
|
172
174
|
this.shareGrants = createOperationScopedFacade(stores.shareGrants, scope)
|
|
173
175
|
this.shareSessions = createOperationScopedFacade(stores.shareSessions, scope)
|
|
174
176
|
this.connectorConnections = createOperationScopedFacade(stores.connectorConnections, scope)
|
|
177
|
+
this.fileUploadSessions = createOperationScopedFacade(stores.fileUploadSessions, scope)
|
|
175
178
|
this.migrators = options.path ? createSqliteStorageMigrators(options.path) : []
|
|
176
179
|
registerSqliteStorageTestingAdapter(this, (durationMs) =>
|
|
177
180
|
stores.connectorConnections.advanceTimeForTesting(durationMs)
|
|
@@ -333,6 +336,7 @@ function createSqliteStores(
|
|
|
333
336
|
shareGrants,
|
|
334
337
|
shareSessions,
|
|
335
338
|
connectorConnections: new SqliteConnectorConnectionStorage(connection),
|
|
339
|
+
fileUploadSessions: new SqliteFileUploadSessionStorage(connection),
|
|
336
340
|
}
|
|
337
341
|
}
|
|
338
342
|
|
|
@@ -357,6 +361,7 @@ interface SqliteStoreSet {
|
|
|
357
361
|
readonly shareGrants: SqliteShareGrantStorage
|
|
358
362
|
readonly shareSessions: SqliteShareSessionStorage
|
|
359
363
|
readonly connectorConnections: SqliteConnectorConnectionStorage
|
|
364
|
+
readonly fileUploadSessions: SqliteFileUploadSessionStorage
|
|
360
365
|
}
|
|
361
366
|
|
|
362
367
|
export { migrateSqliteStorage } from "./migrations"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
CREATE TABLE object_vectors (
|
|
2
|
+
project_id TEXT NOT NULL,
|
|
3
|
+
object_type_id TEXT NOT NULL,
|
|
4
|
+
primary_id TEXT NOT NULL,
|
|
5
|
+
profile TEXT NOT NULL CHECK (length(trim(profile)) > 0),
|
|
6
|
+
configuration TEXT NOT NULL,
|
|
7
|
+
source TEXT NOT NULL CHECK (
|
|
8
|
+
json_valid(source) AND json_type(source) = 'array' AND json_array_length(source) > 0
|
|
9
|
+
),
|
|
10
|
+
source_fingerprint TEXT NOT NULL,
|
|
11
|
+
embedding BLOB NOT NULL CHECK (
|
|
12
|
+
typeof(embedding) = 'blob' AND length(embedding) BETWEEN 4 AND 64000
|
|
13
|
+
AND length(embedding) % 4 = 0
|
|
14
|
+
),
|
|
15
|
+
last_commit_id TEXT NOT NULL,
|
|
16
|
+
PRIMARY KEY (project_id, object_type_id, primary_id, profile),
|
|
17
|
+
FOREIGN KEY (project_id, object_type_id, primary_id)
|
|
18
|
+
REFERENCES objects (project_id, object_type_id, primary_id) ON DELETE CASCADE,
|
|
19
|
+
FOREIGN KEY (project_id, last_commit_id)
|
|
20
|
+
REFERENCES ontology_commits (project_id, id) DEFERRABLE INITIALLY DEFERRED
|
|
21
|
+
);
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
CREATE TABLE object_vector_indexing (
|
|
2
|
+
project_id TEXT NOT NULL,
|
|
3
|
+
id TEXT NOT NULL,
|
|
4
|
+
object_type_id TEXT NOT NULL,
|
|
5
|
+
primary_id TEXT NOT NULL,
|
|
6
|
+
profile TEXT NOT NULL,
|
|
7
|
+
request TEXT NOT NULL,
|
|
8
|
+
status TEXT NOT NULL CHECK (status IN ('pending', 'running', 'ready', 'failed')),
|
|
9
|
+
available_at TEXT NOT NULL,
|
|
10
|
+
dispatch_at TEXT NOT NULL,
|
|
11
|
+
values_json TEXT,
|
|
12
|
+
error TEXT,
|
|
13
|
+
PRIMARY KEY (project_id, id),
|
|
14
|
+
UNIQUE (project_id, object_type_id, primary_id, profile)
|
|
15
|
+
);
|
|
16
|
+
CREATE INDEX object_vector_indexing_due ON object_vector_indexing (project_id, dispatch_at, id)
|
|
17
|
+
WHERE status <> 'failed';
|
|
18
|
+
|
|
19
|
+
CREATE TABLE executions_v2 (
|
|
20
|
+
project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
|
|
21
|
+
id TEXT NOT NULL CHECK (length(trim(id)) > 0),
|
|
22
|
+
executor_kind TEXT NOT NULL CHECK (
|
|
23
|
+
executor_kind IN (
|
|
24
|
+
'request', 'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow',
|
|
25
|
+
'agent', 'kernel'
|
|
26
|
+
)
|
|
27
|
+
),
|
|
28
|
+
executor_id TEXT NOT NULL CHECK (length(trim(executor_id)) > 0),
|
|
29
|
+
source_kind TEXT NOT NULL CHECK (
|
|
30
|
+
source_kind IN ('http', 'webhook', 'schedule', 'event', 'datasetVersion', 'execution', 'ontologyCommit')
|
|
31
|
+
),
|
|
32
|
+
source_id TEXT NOT NULL CHECK (length(trim(source_id)) > 0),
|
|
33
|
+
requested_by_user_id TEXT,
|
|
34
|
+
requested_by_service_account_id TEXT,
|
|
35
|
+
correlation_id TEXT NOT NULL CHECK (length(trim(correlation_id)) > 0),
|
|
36
|
+
parent_execution_id TEXT,
|
|
37
|
+
authority_kind TEXT NOT NULL CHECK (
|
|
38
|
+
authority_kind IN ('principal', 'trustedPrimitive', 'delegated', 'kernel', 'disabled')
|
|
39
|
+
),
|
|
40
|
+
authority_user_id TEXT,
|
|
41
|
+
authority_service_account_id TEXT,
|
|
42
|
+
authority_session_id TEXT,
|
|
43
|
+
authority_access_token_id TEXT,
|
|
44
|
+
authority_primitive_kind TEXT CHECK (
|
|
45
|
+
authority_primitive_kind IS NULL
|
|
46
|
+
OR authority_primitive_kind IN (
|
|
47
|
+
'action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow'
|
|
48
|
+
)
|
|
49
|
+
),
|
|
50
|
+
authority_primitive_id TEXT,
|
|
51
|
+
authority_kernel_operation TEXT CHECK (
|
|
52
|
+
authority_kernel_operation IS NULL OR authority_kernel_operation IN ('ontology.recover', 'ontology.indexVectors')
|
|
53
|
+
),
|
|
54
|
+
authority_delegation_kind TEXT CHECK (
|
|
55
|
+
authority_delegation_kind IS NULL OR authority_delegation_kind = 'share'
|
|
56
|
+
),
|
|
57
|
+
authority_delegation_id TEXT,
|
|
58
|
+
authority_delegation_session_id TEXT,
|
|
59
|
+
requester_group_ids TEXT NOT NULL DEFAULT '[]',
|
|
60
|
+
created_at TEXT NOT NULL,
|
|
61
|
+
PRIMARY KEY (project_id, id),
|
|
62
|
+
FOREIGN KEY (project_id, parent_execution_id) REFERENCES executions (project_id, id),
|
|
63
|
+
FOREIGN KEY (project_id, requested_by_user_id) REFERENCES auth_users (project_id, id),
|
|
64
|
+
FOREIGN KEY (project_id, requested_by_service_account_id)
|
|
65
|
+
REFERENCES auth_service_accounts (project_id, id),
|
|
66
|
+
FOREIGN KEY (project_id, authority_user_id) REFERENCES auth_users (project_id, id),
|
|
67
|
+
FOREIGN KEY (project_id, authority_service_account_id)
|
|
68
|
+
REFERENCES auth_service_accounts (project_id, id),
|
|
69
|
+
FOREIGN KEY (project_id, authority_session_id) REFERENCES auth_sessions (project_id, id),
|
|
70
|
+
FOREIGN KEY (project_id, authority_access_token_id)
|
|
71
|
+
REFERENCES auth_access_tokens (project_id, id),
|
|
72
|
+
FOREIGN KEY (
|
|
73
|
+
project_id, authority_delegation_session_id, authority_delegation_id
|
|
74
|
+
) REFERENCES share_sessions (project_id, id, grant_id),
|
|
75
|
+
CHECK ((requested_by_user_id IS NOT NULL) + (requested_by_service_account_id IS NOT NULL) <= 1),
|
|
76
|
+
CHECK (
|
|
77
|
+
(source_kind = 'execution' AND parent_execution_id = source_id)
|
|
78
|
+
OR (source_kind <> 'execution' AND parent_execution_id IS NULL)
|
|
79
|
+
),
|
|
80
|
+
CHECK (
|
|
81
|
+
(
|
|
82
|
+
authority_kind = 'principal'
|
|
83
|
+
AND (authority_user_id IS NOT NULL) + (authority_service_account_id IS NOT NULL) = 1
|
|
84
|
+
AND (authority_session_id IS NOT NULL) + (authority_access_token_id IS NOT NULL) <= 1
|
|
85
|
+
AND (authority_session_id IS NULL OR authority_user_id IS NOT NULL)
|
|
86
|
+
AND authority_primitive_kind IS NULL
|
|
87
|
+
AND authority_primitive_id IS NULL
|
|
88
|
+
AND authority_kernel_operation IS NULL
|
|
89
|
+
AND authority_delegation_kind IS NULL
|
|
90
|
+
AND authority_delegation_id IS NULL
|
|
91
|
+
AND authority_delegation_session_id IS NULL
|
|
92
|
+
)
|
|
93
|
+
OR (
|
|
94
|
+
authority_kind = 'trustedPrimitive'
|
|
95
|
+
AND authority_user_id IS NULL
|
|
96
|
+
AND authority_service_account_id IS NULL
|
|
97
|
+
AND authority_session_id IS NULL
|
|
98
|
+
AND authority_access_token_id IS NULL
|
|
99
|
+
AND authority_primitive_kind IS NOT NULL
|
|
100
|
+
AND authority_primitive_id IS NOT NULL
|
|
101
|
+
AND length(trim(authority_primitive_id)) > 0
|
|
102
|
+
AND authority_kernel_operation IS NULL
|
|
103
|
+
AND authority_delegation_kind IS NULL
|
|
104
|
+
AND authority_delegation_id IS NULL
|
|
105
|
+
AND authority_delegation_session_id IS NULL
|
|
106
|
+
)
|
|
107
|
+
OR (
|
|
108
|
+
authority_kind = 'delegated'
|
|
109
|
+
AND authority_user_id IS NULL
|
|
110
|
+
AND authority_service_account_id IS NULL
|
|
111
|
+
AND authority_session_id IS NULL
|
|
112
|
+
AND authority_access_token_id IS NULL
|
|
113
|
+
AND authority_primitive_kind IS NULL
|
|
114
|
+
AND authority_primitive_id IS NULL
|
|
115
|
+
AND authority_kernel_operation IS NULL
|
|
116
|
+
AND authority_delegation_kind IS NOT NULL
|
|
117
|
+
AND authority_delegation_kind = 'share'
|
|
118
|
+
AND authority_delegation_id IS NOT NULL
|
|
119
|
+
AND length(trim(authority_delegation_id)) > 0
|
|
120
|
+
AND authority_delegation_session_id IS NOT NULL
|
|
121
|
+
AND length(trim(authority_delegation_session_id)) > 0
|
|
122
|
+
)
|
|
123
|
+
OR (
|
|
124
|
+
authority_kind = 'kernel'
|
|
125
|
+
AND authority_user_id IS NULL
|
|
126
|
+
AND authority_service_account_id IS NULL
|
|
127
|
+
AND authority_session_id IS NULL
|
|
128
|
+
AND authority_access_token_id IS NULL
|
|
129
|
+
AND authority_primitive_kind IS NULL
|
|
130
|
+
AND authority_primitive_id IS NULL
|
|
131
|
+
AND authority_kernel_operation IS NOT NULL
|
|
132
|
+
AND authority_delegation_kind IS NULL
|
|
133
|
+
AND authority_delegation_id IS NULL
|
|
134
|
+
AND authority_delegation_session_id IS NULL
|
|
135
|
+
)
|
|
136
|
+
OR (
|
|
137
|
+
authority_kind = 'disabled'
|
|
138
|
+
AND authority_user_id IS NULL
|
|
139
|
+
AND authority_service_account_id IS NULL
|
|
140
|
+
AND authority_session_id IS NULL
|
|
141
|
+
AND authority_access_token_id IS NULL
|
|
142
|
+
AND authority_primitive_kind IS NULL
|
|
143
|
+
AND authority_primitive_id IS NULL
|
|
144
|
+
AND authority_kernel_operation IS NULL
|
|
145
|
+
AND authority_delegation_kind IS NULL
|
|
146
|
+
AND authority_delegation_id IS NULL
|
|
147
|
+
AND authority_delegation_session_id IS NULL
|
|
148
|
+
)
|
|
149
|
+
),
|
|
150
|
+
CHECK (
|
|
151
|
+
(
|
|
152
|
+
executor_kind = 'request'
|
|
153
|
+
AND source_kind = 'http'
|
|
154
|
+
AND executor_id = source_id
|
|
155
|
+
AND (
|
|
156
|
+
(
|
|
157
|
+
authority_kind = 'principal'
|
|
158
|
+
AND requested_by_user_id IS authority_user_id
|
|
159
|
+
AND requested_by_service_account_id IS authority_service_account_id
|
|
160
|
+
)
|
|
161
|
+
OR (
|
|
162
|
+
authority_kind IN ('delegated', 'disabled')
|
|
163
|
+
AND requested_by_user_id IS NULL
|
|
164
|
+
AND requested_by_service_account_id IS NULL
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
OR (
|
|
169
|
+
executor_kind IN ('action', 'pipeline', 'projection', 'rule', 'sync', 'webhook', 'workflow')
|
|
170
|
+
AND authority_kind = 'trustedPrimitive'
|
|
171
|
+
AND executor_kind = authority_primitive_kind
|
|
172
|
+
)
|
|
173
|
+
OR (
|
|
174
|
+
executor_kind = 'agent'
|
|
175
|
+
AND source_kind = 'execution'
|
|
176
|
+
AND (
|
|
177
|
+
(
|
|
178
|
+
authority_kind = 'principal'
|
|
179
|
+
AND authority_service_account_id IS NOT NULL
|
|
180
|
+
AND authority_session_id IS NULL
|
|
181
|
+
AND authority_access_token_id IS NULL
|
|
182
|
+
)
|
|
183
|
+
OR (
|
|
184
|
+
authority_kind = 'principal'
|
|
185
|
+
AND authority_user_id IS NOT NULL
|
|
186
|
+
AND (authority_session_id IS NOT NULL) + (authority_access_token_id IS NOT NULL) = 1
|
|
187
|
+
)
|
|
188
|
+
OR authority_kind = 'disabled'
|
|
189
|
+
)
|
|
190
|
+
)
|
|
191
|
+
OR (executor_kind = 'kernel' AND authority_kind = 'kernel')
|
|
192
|
+
)
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
INSERT INTO executions_v2 (project_id, id, executor_kind, executor_id, source_kind, source_id, requested_by_user_id, requested_by_service_account_id, correlation_id, parent_execution_id, authority_kind, authority_user_id, authority_service_account_id, authority_session_id, authority_access_token_id, authority_primitive_kind, authority_primitive_id, authority_kernel_operation, authority_delegation_kind, authority_delegation_id, authority_delegation_session_id, requester_group_ids, created_at) SELECT project_id, id, executor_kind, executor_id, source_kind, source_id, requested_by_user_id, requested_by_service_account_id, correlation_id, parent_execution_id, authority_kind, authority_user_id, authority_service_account_id, authority_session_id, authority_access_token_id, authority_primitive_kind, authority_primitive_id, authority_kernel_operation, authority_delegation_kind, authority_delegation_id, authority_delegation_session_id, requester_group_ids, created_at FROM executions;
|
|
196
|
+
DROP TABLE executions;
|
|
197
|
+
ALTER TABLE executions_v2 RENAME TO executions;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Intervention actors were stored as { principalType, principalId }. Rewrite them to the
|
|
2
|
+
-- canonical Principal shape { type, id } used by every other stored principal.
|
|
3
|
+
UPDATE workflow_interventions
|
|
4
|
+
SET submitted_by = json_object(
|
|
5
|
+
'type', json_extract(submitted_by, '$.principalType'),
|
|
6
|
+
'id', json_extract(submitted_by, '$.principalId')
|
|
7
|
+
)
|
|
8
|
+
WHERE json_extract(submitted_by, '$.principalType') IS NOT NULL;
|
|
9
|
+
|
|
10
|
+
UPDATE workflow_interventions
|
|
11
|
+
SET cancelled_by = json_object(
|
|
12
|
+
'type', json_extract(cancelled_by, '$.principalType'),
|
|
13
|
+
'id', json_extract(cancelled_by, '$.principalId')
|
|
14
|
+
)
|
|
15
|
+
WHERE json_extract(cancelled_by, '$.principalType') IS NOT NULL;
|