@sixb/sqlite 0.1.18 → 0.1.20
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/dist/action-run-storage.d.ts +4 -9
- package/dist/action-run-storage.d.ts.map +1 -1
- package/dist/agents/agent-storage.d.ts.map +1 -1
- package/dist/ai-limit-storage.d.ts.map +1 -1
- package/dist/ai-usage-storage.d.ts.map +1 -1
- package/dist/auth-storage/auth-storage.d.ts.map +1 -1
- package/dist/auth-storage/magic-links.d.ts.map +1 -1
- package/dist/auth-storage/oidc-attempts.d.ts.map +1 -1
- package/dist/auth-storage/rows.d.ts +0 -3
- package/dist/auth-storage/rows.d.ts.map +1 -1
- package/dist/auth-storage/sessions.d.ts +0 -10
- package/dist/auth-storage/sessions.d.ts.map +1 -1
- package/dist/auth-storage/shared.d.ts +0 -2
- package/dist/auth-storage/shared.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +301 -398
- package/dist/index.js.map +27 -27
- package/dist/migrations.d.ts +2 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/objects/storage.d.ts.map +1 -1
- package/dist/pipeline-run-storage.d.ts.map +1 -1
- package/dist/projection-run-storage.d.ts.map +1 -1
- package/dist/rules-storage.d.ts.map +1 -1
- package/dist/share-grant-storage.d.ts.map +1 -1
- package/dist/share-session-storage.d.ts.map +1 -1
- package/dist/sync-run-storage.d.ts.map +1 -1
- package/dist/timeseries-storage.d.ts.map +1 -1
- package/dist/transactions.d.ts +0 -1
- package/dist/transactions.d.ts.map +1 -1
- package/dist/webhook-run-storage.d.ts.map +1 -1
- package/dist/workflow-intervention-storage.d.ts.map +1 -1
- package/dist/workflow-run-storage.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/action-run-storage.ts +104 -447
- package/src/agents/agent-storage.ts +0 -5
- package/src/ai-limit-storage.ts +0 -2
- package/src/ai-usage-storage.ts +0 -4
- package/src/auth-storage/auth-storage.ts +0 -23
- package/src/auth-storage/magic-links.ts +1 -6
- package/src/auth-storage/oidc-attempts.ts +1 -6
- package/src/auth-storage/rows.ts +0 -6
- package/src/auth-storage/sessions.ts +1 -49
- package/src/auth-storage/shared.ts +3 -20
- package/src/index.ts +1 -10
- package/src/migrations/060-terminal-action-runs.sql +149 -0
- package/src/migrations/061-drop-auth-audience.sql +16 -0
- package/src/migrations.ts +24 -0
- package/src/objects/storage.ts +0 -5
- package/src/pipeline-run-storage.ts +0 -5
- package/src/projection-run-storage.ts +0 -5
- package/src/rules-storage.ts +0 -5
- package/src/share-grant-storage.ts +0 -2
- package/src/share-session-storage.ts +0 -2
- package/src/sync-run-storage.ts +0 -5
- package/src/timeseries-storage.ts +0 -5
- package/src/transactions.ts +8 -8
- package/src/webhook-run-storage.ts +0 -3
- package/src/workflow-intervention-storage.ts +0 -5
- package/src/workflow-run-storage.ts +0 -5
|
@@ -5,32 +5,21 @@ import {
|
|
|
5
5
|
serializeActionRunFailure,
|
|
6
6
|
} from "@sixb/core/internal/action-run-storage"
|
|
7
7
|
import { assertActionRunExecution } from "@sixb/core/internal/action-run-storage-provider"
|
|
8
|
-
import {
|
|
9
|
-
actionRunPhaseRecordsEqual,
|
|
10
|
-
canRequeueActionRunAfterEnqueueFailure,
|
|
11
|
-
finishActionRunPhase,
|
|
12
|
-
} from "@sixb/core/internal/storage"
|
|
8
|
+
import { normalizeRecordActionRunInput, resolveActionRunEffects } from "@sixb/core/internal/storage"
|
|
13
9
|
import type {
|
|
14
10
|
ActionRunEffectsRecord,
|
|
15
|
-
ActionRunFailure,
|
|
16
|
-
ActionRunParams,
|
|
17
11
|
ActionRunPhase,
|
|
18
12
|
ActionRunRecord,
|
|
13
|
+
ActionRunStatus,
|
|
19
14
|
ActionRunStorage,
|
|
20
15
|
ActionRunWritebackRecord,
|
|
21
|
-
EnterActionRunPhaseInput,
|
|
22
16
|
ExecutionStorage,
|
|
23
|
-
FinishActionRunInput,
|
|
24
17
|
ListActionRunsInput,
|
|
25
18
|
ListActionRunsResult,
|
|
26
|
-
LockActionMaterializationRunInput,
|
|
27
|
-
QueueActionRunInput,
|
|
28
19
|
RecordActionEffectsInput,
|
|
29
|
-
|
|
30
|
-
StartActionRunInput,
|
|
20
|
+
RecordActionRunInput,
|
|
31
21
|
} from "@sixb/core/storage"
|
|
32
|
-
import { ActionRunError
|
|
33
|
-
import { installFreshSqliteSchema } from "./migrations"
|
|
22
|
+
import { ActionRunError } from "@sixb/core/storage"
|
|
34
23
|
import {
|
|
35
24
|
closeSqliteStoreConnection,
|
|
36
25
|
openSqliteStoreConnection,
|
|
@@ -56,299 +45,86 @@ export class SqliteActionRunStorage implements ActionRunStorage {
|
|
|
56
45
|
this.connection = openSqliteStoreConnection(options)
|
|
57
46
|
this.db = this.connection.db
|
|
58
47
|
this.executions = options.executions
|
|
59
|
-
|
|
60
|
-
if (this.connection.installFreshSchema) {
|
|
61
|
-
installFreshSqliteSchema(this.db)
|
|
62
|
-
}
|
|
63
48
|
}
|
|
64
49
|
|
|
65
|
-
async
|
|
66
|
-
const
|
|
67
|
-
.query(`SELECT action_id, status FROM action_runs WHERE project_id = ? AND id = ?`)
|
|
68
|
-
.get(input.projectId, input.runId) as {
|
|
69
|
-
readonly action_id: string
|
|
70
|
-
readonly status: string
|
|
71
|
-
} | null
|
|
72
|
-
if (!row) {
|
|
73
|
-
throw new ActionRunError(
|
|
74
|
-
`[SixbSqlite] Action run '${input.runId}' not found for project '${input.projectId}'.`
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
if (row.action_id !== input.actionId) {
|
|
78
|
-
throw new ActionRunError(
|
|
79
|
-
`[SixbSqlite] Action run '${input.runId}' does not belong to action '${input.actionId}'.`
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
if (row.status !== "running") {
|
|
83
|
-
throw new ActionRunError(
|
|
84
|
-
`[SixbSqlite] Action run '${input.runId}' cannot materialize from status '${row.status}'.`
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
const record = await this.getById({ projectId: input.projectId, id: input.runId })
|
|
88
|
-
if (!record) {
|
|
89
|
-
throw new ActionRunError(
|
|
90
|
-
`[SixbSqlite] Action run '${input.runId}' disappeared while locking materialization.`
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
return record
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async queue(input: QueueActionRunInput): Promise<ActionRunRecord> {
|
|
97
|
-
const queuedAt = input.queuedAt ?? new Date()
|
|
50
|
+
async record(input: RecordActionRunInput): Promise<ActionRunRecord> {
|
|
51
|
+
const record = normalizeRecordActionRunInput(input)
|
|
98
52
|
await assertActionRunExecution({
|
|
99
53
|
executions: this.executions,
|
|
100
|
-
projectId:
|
|
101
|
-
executionId:
|
|
102
|
-
runId:
|
|
103
|
-
actionId:
|
|
54
|
+
projectId: record.projectId,
|
|
55
|
+
executionId: record.executionId,
|
|
56
|
+
runId: record.id,
|
|
57
|
+
actionId: record.actionId,
|
|
104
58
|
})
|
|
105
59
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
.query(
|
|
109
|
-
`
|
|
110
|
-
INSERT INTO action_runs (
|
|
111
|
-
project_id,
|
|
112
|
-
id,
|
|
113
|
-
execution_id,
|
|
114
|
-
action_id,
|
|
115
|
-
subject_kind,
|
|
116
|
-
object_type_id,
|
|
117
|
-
primary_id,
|
|
118
|
-
status,
|
|
119
|
-
phase,
|
|
120
|
-
queued_at,
|
|
121
|
-
started_at,
|
|
122
|
-
finished_at,
|
|
123
|
-
params,
|
|
124
|
-
idempotency_key,
|
|
125
|
-
writeback_status,
|
|
126
|
-
writeback_completed_at,
|
|
127
|
-
writeback_result,
|
|
128
|
-
writeback_error,
|
|
129
|
-
effects_status,
|
|
130
|
-
effects_completed_at,
|
|
131
|
-
effects_error,
|
|
132
|
-
error
|
|
133
|
-
) VALUES (
|
|
134
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, ?,
|
|
135
|
-
NULL, NULL, NULL, NULL,
|
|
136
|
-
NULL, NULL, NULL,
|
|
137
|
-
NULL
|
|
138
|
-
)
|
|
60
|
+
const inserted = this.db
|
|
61
|
+
.query(
|
|
139
62
|
`
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
63
|
+
INSERT INTO action_runs (
|
|
64
|
+
project_id,
|
|
65
|
+
id,
|
|
66
|
+
execution_id,
|
|
67
|
+
action_id,
|
|
68
|
+
subject_kind,
|
|
69
|
+
object_type_id,
|
|
70
|
+
primary_id,
|
|
71
|
+
status,
|
|
72
|
+
phase,
|
|
73
|
+
started_at,
|
|
74
|
+
finished_at,
|
|
75
|
+
params,
|
|
76
|
+
idempotency_key,
|
|
77
|
+
writeback_status,
|
|
78
|
+
writeback_completed_at,
|
|
79
|
+
writeback_result,
|
|
80
|
+
writeback_error,
|
|
81
|
+
error
|
|
82
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
83
|
+
ON CONFLICT DO NOTHING
|
|
84
|
+
`
|
|
85
|
+
)
|
|
86
|
+
.run(
|
|
87
|
+
record.projectId,
|
|
88
|
+
record.id,
|
|
89
|
+
record.executionId,
|
|
90
|
+
record.actionId,
|
|
91
|
+
record.subject.kind,
|
|
92
|
+
record.subject.kind === "object" ? record.subject.objectTypeId : null,
|
|
93
|
+
record.subject.kind === "object" ? record.subject.primaryId : null,
|
|
94
|
+
record.status,
|
|
95
|
+
record.phase,
|
|
96
|
+
record.startedAt.toISOString(),
|
|
97
|
+
record.finishedAt.toISOString(),
|
|
98
|
+
JSON.stringify(record.params),
|
|
99
|
+
record.idempotencyKey,
|
|
100
|
+
record.writeback?.status ?? null,
|
|
101
|
+
record.writeback?.completedAt.toISOString() ?? null,
|
|
102
|
+
record.writeback?.status === "succeeded" ? JSON.stringify(record.writeback.result) : null,
|
|
103
|
+
record.writeback?.status === "failed"
|
|
104
|
+
? serializeActionRunFailure(record.writeback.error, "writeback")
|
|
105
|
+
: null,
|
|
106
|
+
record.status === "failed" ? serializeActionRunFailure(record.error) : null
|
|
107
|
+
)
|
|
162
108
|
|
|
163
|
-
|
|
164
|
-
|
|
109
|
+
if (inserted.changes === 0) {
|
|
110
|
+
const conflict = this.selectRow(record.projectId, record.id)
|
|
165
111
|
throw new ActionRunError(
|
|
166
|
-
|
|
112
|
+
conflict
|
|
113
|
+
? `[SixbSqlite] Action run '${record.id}' is already recorded for project '${record.projectId}'.`
|
|
114
|
+
: `[SixbSqlite] Execution '${record.executionId}' already belongs to another Action run.`
|
|
167
115
|
)
|
|
168
116
|
}
|
|
169
|
-
|
|
170
|
-
return record
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private async requeueAfterEnqueueFailure(
|
|
174
|
-
input: QueueActionRunInput,
|
|
175
|
-
queuedAt: Date
|
|
176
|
-
): Promise<ActionRunRecord> {
|
|
177
|
-
return this.db.transaction(() => {
|
|
178
|
-
const existing = this.db
|
|
179
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
180
|
-
.get(input.projectId, input.id) as DatabaseRow | null
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
!existing ||
|
|
184
|
-
!canRequeueActionRunAfterEnqueueFailure(rowToActionRunRecord(existing), input)
|
|
185
|
-
) {
|
|
186
|
-
throw new ActionRunError(
|
|
187
|
-
`[SixbSqlite] Action run '${input.id}' already exists for project '${input.projectId}'.`
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
this.db
|
|
192
|
-
.query(
|
|
193
|
-
`
|
|
194
|
-
UPDATE action_runs
|
|
195
|
-
SET
|
|
196
|
-
status = ?,
|
|
197
|
-
phase = ?,
|
|
198
|
-
queued_at = ?,
|
|
199
|
-
started_at = NULL,
|
|
200
|
-
finished_at = NULL,
|
|
201
|
-
writeback_status = NULL,
|
|
202
|
-
writeback_completed_at = NULL,
|
|
203
|
-
writeback_result = NULL,
|
|
204
|
-
writeback_error = NULL,
|
|
205
|
-
effects_status = NULL,
|
|
206
|
-
effects_completed_at = NULL,
|
|
207
|
-
effects_error = NULL,
|
|
208
|
-
error = NULL
|
|
209
|
-
WHERE project_id = ? AND id = ?
|
|
210
|
-
`
|
|
211
|
-
)
|
|
212
|
-
.run("queued", "request", queuedAt.toISOString(), input.projectId, input.id)
|
|
213
|
-
|
|
214
|
-
const updated = this.db
|
|
215
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
216
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
217
|
-
|
|
218
|
-
return rowToActionRunRecord(updated)
|
|
219
|
-
})()
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async start(input: StartActionRunInput): Promise<ActionRunRecord> {
|
|
223
|
-
return this.db.transaction(() => {
|
|
224
|
-
const existing = this.db
|
|
225
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
226
|
-
.get(input.projectId, input.id) as DatabaseRow | null
|
|
227
|
-
|
|
228
|
-
if (!existing) {
|
|
229
|
-
throw new ActionRunError(
|
|
230
|
-
`[SixbSqlite] Action run '${input.id}' not found for project '${input.projectId}'.`
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (existing.status !== "queued") {
|
|
235
|
-
throw new ActionRunError(
|
|
236
|
-
`[SixbSqlite] Action run '${input.id}' cannot start from status '${existing.status}'.`
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
this.db
|
|
241
|
-
.query(
|
|
242
|
-
`
|
|
243
|
-
UPDATE action_runs
|
|
244
|
-
SET
|
|
245
|
-
status = ?,
|
|
246
|
-
phase = ?,
|
|
247
|
-
started_at = ?,
|
|
248
|
-
error = NULL
|
|
249
|
-
WHERE project_id = ? AND id = ?
|
|
250
|
-
`
|
|
251
|
-
)
|
|
252
|
-
.run(
|
|
253
|
-
"running",
|
|
254
|
-
input.phase ?? "validation",
|
|
255
|
-
(input.startedAt ?? new Date()).toISOString(),
|
|
256
|
-
input.projectId,
|
|
257
|
-
input.id
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
const updated = this.db
|
|
261
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
262
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
263
|
-
|
|
264
|
-
return rowToActionRunRecord(updated)
|
|
265
|
-
})()
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async enterPhase(input: EnterActionRunPhaseInput): Promise<ActionRunRecord> {
|
|
269
|
-
return this.db.transaction(() => {
|
|
270
|
-
this.requireRunningRun(input.projectId, input.id, "transition phase")
|
|
271
|
-
|
|
272
|
-
this.db
|
|
273
|
-
.query(
|
|
274
|
-
`
|
|
275
|
-
UPDATE action_runs
|
|
276
|
-
SET phase = ?
|
|
277
|
-
WHERE project_id = ? AND id = ?
|
|
278
|
-
`
|
|
279
|
-
)
|
|
280
|
-
.run(input.phase, input.projectId, input.id)
|
|
281
|
-
|
|
282
|
-
const updated = this.db
|
|
283
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
284
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
285
|
-
|
|
286
|
-
return rowToActionRunRecord(updated)
|
|
287
|
-
})()
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async recordWriteback(input: RecordActionWritebackInput): Promise<ActionRunRecord> {
|
|
291
|
-
return this.db.transaction(() => {
|
|
292
|
-
const existing = this.requireRunningRun(input.projectId, input.id, "record writeback")
|
|
293
|
-
const nextWriteback = toWritebackRecord(input, new Date(input.completedAt ?? new Date()))
|
|
294
|
-
const currentWriteback = toActionRunWritebackRecord(existing)
|
|
295
|
-
|
|
296
|
-
if (currentWriteback) {
|
|
297
|
-
if (actionRunPhaseRecordsEqual(currentWriteback, nextWriteback)) {
|
|
298
|
-
return rowToActionRunRecord(existing)
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
throw new ActionRunError(
|
|
302
|
-
`[SixbSqlite] Action run '${input.id}' already has a different writeback record.`
|
|
303
|
-
)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
this.db
|
|
307
|
-
.query(
|
|
308
|
-
`
|
|
309
|
-
UPDATE action_runs
|
|
310
|
-
SET
|
|
311
|
-
phase = ?,
|
|
312
|
-
writeback_status = ?,
|
|
313
|
-
writeback_completed_at = ?,
|
|
314
|
-
writeback_result = ?,
|
|
315
|
-
writeback_error = ?
|
|
316
|
-
WHERE project_id = ? AND id = ?
|
|
317
|
-
`
|
|
318
|
-
)
|
|
319
|
-
.run(
|
|
320
|
-
"writeback",
|
|
321
|
-
input.status,
|
|
322
|
-
nextWriteback.completedAt.toISOString(),
|
|
323
|
-
input.status === "succeeded" ? serializeJsonValue(input.result) : null,
|
|
324
|
-
input.status === "failed" ? serializeActionRunFailure(input.error, "writeback") : null,
|
|
325
|
-
input.projectId,
|
|
326
|
-
input.id
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
const updated = this.db
|
|
330
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
331
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
332
|
-
|
|
333
|
-
return rowToActionRunRecord(updated)
|
|
334
|
-
})()
|
|
117
|
+
return rowToActionRunRecord(this.requireRow(record.projectId, record.id))
|
|
335
118
|
}
|
|
336
119
|
|
|
337
120
|
async recordEffects(input: RecordActionEffectsInput): Promise<ActionRunRecord> {
|
|
338
121
|
return this.db.transaction(() => {
|
|
339
|
-
const existing = this.
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return rowToActionRunRecord(existing)
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
throw new ActionRunError(
|
|
349
|
-
`[SixbSqlite] Action run '${input.id}' already has a different effects record.`
|
|
350
|
-
)
|
|
351
|
-
}
|
|
122
|
+
const existing = this.selectRow(input.projectId, input.id)
|
|
123
|
+
const { run, effects } = resolveActionRunEffects(
|
|
124
|
+
existing ? rowToActionRunRecord(existing) : null,
|
|
125
|
+
input
|
|
126
|
+
)
|
|
127
|
+
if (!effects) return run
|
|
352
128
|
|
|
353
129
|
this.db
|
|
354
130
|
.query(
|
|
@@ -364,67 +140,13 @@ export class SqliteActionRunStorage implements ActionRunStorage {
|
|
|
364
140
|
)
|
|
365
141
|
.run(
|
|
366
142
|
"effects",
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
input.projectId,
|
|
371
|
-
input.id
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
const updated = this.db
|
|
375
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
376
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
377
|
-
|
|
378
|
-
return rowToActionRunRecord(updated)
|
|
379
|
-
})()
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
async finish(input: FinishActionRunInput): Promise<ActionRunRecord> {
|
|
383
|
-
return this.db.transaction(() => {
|
|
384
|
-
const existing = this.db
|
|
385
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
386
|
-
.get(input.projectId, input.id) as DatabaseRow | null
|
|
387
|
-
|
|
388
|
-
if (!existing) {
|
|
389
|
-
throw new ActionRunError(
|
|
390
|
-
`[SixbSqlite] Action run '${input.id}' not found for project '${input.projectId}'.`
|
|
391
|
-
)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (isTerminalActionRun({ status: existing.status })) {
|
|
395
|
-
throw new ActionRunError(
|
|
396
|
-
`[SixbSqlite] Action run '${input.id}' cannot finish from terminal status '${existing.status}'.`
|
|
397
|
-
)
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
const phase = finishActionRunPhase(input, existing.phase)
|
|
401
|
-
|
|
402
|
-
this.db
|
|
403
|
-
.query(
|
|
404
|
-
`
|
|
405
|
-
UPDATE action_runs
|
|
406
|
-
SET
|
|
407
|
-
status = ?,
|
|
408
|
-
phase = ?,
|
|
409
|
-
finished_at = ?,
|
|
410
|
-
error = ?
|
|
411
|
-
WHERE project_id = ? AND id = ?
|
|
412
|
-
`
|
|
413
|
-
)
|
|
414
|
-
.run(
|
|
415
|
-
input.status,
|
|
416
|
-
phase,
|
|
417
|
-
(input.finishedAt ?? new Date()).toISOString(),
|
|
418
|
-
input.status === "succeeded" ? null : serializeActionRunFailure(input.error),
|
|
143
|
+
effects.status,
|
|
144
|
+
effects.completedAt.toISOString(),
|
|
145
|
+
effects.status === "failed" ? serializeActionRunFailure(effects.error, "effects") : null,
|
|
419
146
|
input.projectId,
|
|
420
147
|
input.id
|
|
421
148
|
)
|
|
422
|
-
|
|
423
|
-
const updated = this.db
|
|
424
|
-
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
425
|
-
.get(input.projectId, input.id) as DatabaseRow
|
|
426
|
-
|
|
427
|
-
return rowToActionRunRecord(updated)
|
|
149
|
+
return rowToActionRunRecord(this.requireRow(input.projectId, input.id))
|
|
428
150
|
})()
|
|
429
151
|
}
|
|
430
152
|
|
|
@@ -503,12 +225,12 @@ export class SqliteActionRunStorage implements ActionRunStorage {
|
|
|
503
225
|
}
|
|
504
226
|
|
|
505
227
|
if (input.startedAfter) {
|
|
506
|
-
whereClauses.push("
|
|
228
|
+
whereClauses.push("started_at >= ?")
|
|
507
229
|
args.push(input.startedAfter.toISOString())
|
|
508
230
|
}
|
|
509
231
|
|
|
510
232
|
if (input.startedBefore) {
|
|
511
|
-
whereClauses.push("
|
|
233
|
+
whereClauses.push("started_at <= ?")
|
|
512
234
|
args.push(input.startedBefore.toISOString())
|
|
513
235
|
}
|
|
514
236
|
|
|
@@ -524,7 +246,7 @@ export class SqliteActionRunStorage implements ActionRunStorage {
|
|
|
524
246
|
let query = `
|
|
525
247
|
SELECT * FROM action_runs
|
|
526
248
|
${where}
|
|
527
|
-
ORDER BY
|
|
249
|
+
ORDER BY started_at ${order}, id ${order}
|
|
528
250
|
`
|
|
529
251
|
const queryArgs = [...args]
|
|
530
252
|
|
|
@@ -550,51 +272,29 @@ export class SqliteActionRunStorage implements ActionRunStorage {
|
|
|
550
272
|
closeSqliteStoreConnection(this.connection)
|
|
551
273
|
}
|
|
552
274
|
|
|
553
|
-
private
|
|
554
|
-
|
|
275
|
+
private selectRow(projectId: string, id: string): DatabaseRow | null {
|
|
276
|
+
return this.db
|
|
555
277
|
.query("SELECT * FROM action_runs WHERE project_id = ? AND id = ?")
|
|
556
278
|
.get(projectId, id) as DatabaseRow | null
|
|
279
|
+
}
|
|
557
280
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
)
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (existing.status !== "running") {
|
|
281
|
+
private requireRow(projectId: string, id: string): DatabaseRow {
|
|
282
|
+
const row = this.selectRow(projectId, id)
|
|
283
|
+
if (!row) {
|
|
565
284
|
throw new ActionRunError(
|
|
566
|
-
`[SixbSqlite] Action run '${id}'
|
|
285
|
+
`[SixbSqlite] Action run '${id}' disappeared from project '${projectId}' while recording it.`
|
|
567
286
|
)
|
|
568
287
|
}
|
|
569
|
-
|
|
570
|
-
return existing
|
|
288
|
+
return row
|
|
571
289
|
}
|
|
572
290
|
}
|
|
573
291
|
|
|
574
|
-
function serializeJsonValue(value: JsonValue): string {
|
|
575
|
-
return JSON.stringify(value)
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function toActionRunFailure(row: DatabaseRow): ActionRunFailure | undefined {
|
|
579
|
-
return row.error === null ? undefined : parseActionRunFailure(row.error)
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function parsePhaseFailure<TPhase extends Extract<ActionRunPhase, "writeback" | "effects">>(
|
|
583
|
-
value: string | null,
|
|
584
|
-
expectedPhase: TPhase
|
|
585
|
-
): ActionRunFailure<TPhase> {
|
|
586
|
-
return parseActionRunFailure(value, expectedPhase)
|
|
587
|
-
}
|
|
588
|
-
|
|
589
292
|
function toActionRunWritebackRecord(row: DatabaseRow): ActionRunWritebackRecord | undefined {
|
|
590
293
|
if (!row.writeback_status) {
|
|
591
294
|
return undefined
|
|
592
295
|
}
|
|
593
296
|
|
|
594
|
-
const completedAt = row.writeback_completed_at
|
|
595
|
-
? new Date(row.writeback_completed_at)
|
|
596
|
-
: new Date(row.queued_at)
|
|
597
|
-
|
|
297
|
+
const completedAt = new Date(row.writeback_completed_at ?? row.finished_at)
|
|
598
298
|
if (row.writeback_status === "succeeded") {
|
|
599
299
|
return {
|
|
600
300
|
status: "succeeded",
|
|
@@ -607,7 +307,7 @@ function toActionRunWritebackRecord(row: DatabaseRow): ActionRunWritebackRecord
|
|
|
607
307
|
return {
|
|
608
308
|
status: "failed",
|
|
609
309
|
completedAt,
|
|
610
|
-
error:
|
|
310
|
+
error: parseActionRunFailure(row.writeback_error, "writeback"),
|
|
611
311
|
}
|
|
612
312
|
}
|
|
613
313
|
|
|
@@ -616,10 +316,7 @@ function toActionRunEffectsRecord(row: DatabaseRow): ActionRunEffectsRecord | un
|
|
|
616
316
|
return undefined
|
|
617
317
|
}
|
|
618
318
|
|
|
619
|
-
const completedAt = row.effects_completed_at
|
|
620
|
-
? new Date(row.effects_completed_at)
|
|
621
|
-
: new Date(row.queued_at)
|
|
622
|
-
|
|
319
|
+
const completedAt = new Date(row.effects_completed_at ?? row.finished_at)
|
|
623
320
|
if (row.effects_status === "succeeded") {
|
|
624
321
|
return {
|
|
625
322
|
status: "succeeded",
|
|
@@ -630,65 +327,30 @@ function toActionRunEffectsRecord(row: DatabaseRow): ActionRunEffectsRecord | un
|
|
|
630
327
|
return {
|
|
631
328
|
status: "failed",
|
|
632
329
|
completedAt,
|
|
633
|
-
error:
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function toWritebackRecord(
|
|
638
|
-
input: RecordActionWritebackInput,
|
|
639
|
-
completedAt: Date
|
|
640
|
-
): ActionRunWritebackRecord {
|
|
641
|
-
if (input.status === "succeeded") {
|
|
642
|
-
return {
|
|
643
|
-
status: "succeeded",
|
|
644
|
-
completedAt,
|
|
645
|
-
result: input.result,
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
return {
|
|
650
|
-
status: "failed",
|
|
651
|
-
completedAt,
|
|
652
|
-
error: input.error,
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
function toEffectsRecord(
|
|
657
|
-
input: RecordActionEffectsInput,
|
|
658
|
-
completedAt: Date
|
|
659
|
-
): ActionRunEffectsRecord {
|
|
660
|
-
if (input.status === "succeeded") {
|
|
661
|
-
return {
|
|
662
|
-
status: "succeeded",
|
|
663
|
-
completedAt,
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
return {
|
|
668
|
-
status: "failed",
|
|
669
|
-
completedAt,
|
|
670
|
-
error: input.error,
|
|
330
|
+
error: parseActionRunFailure(row.effects_error, "effects"),
|
|
671
331
|
}
|
|
672
332
|
}
|
|
673
333
|
|
|
674
334
|
function rowToActionRunRecord(row: DatabaseRow): ActionRunRecord {
|
|
675
|
-
|
|
335
|
+
const writeback = toActionRunWritebackRecord(row)
|
|
336
|
+
const effects = toActionRunEffectsRecord(row)
|
|
337
|
+
const fields = {
|
|
676
338
|
id: row.id,
|
|
677
339
|
projectId: row.project_id,
|
|
678
340
|
executionId: row.execution_id,
|
|
679
341
|
actionId: row.action_id,
|
|
680
342
|
subject: rowToActionSubject(row),
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
finishedAt: row.finished_at ? new Date(row.finished_at) : undefined,
|
|
686
|
-
params: JSON.parse(row.params) as ActionRunParams,
|
|
343
|
+
phase: row.phase,
|
|
344
|
+
startedAt: new Date(row.started_at),
|
|
345
|
+
finishedAt: new Date(row.finished_at),
|
|
346
|
+
params: JSON.parse(row.params) as ActionRunRecord["params"],
|
|
687
347
|
idempotencyKey: row.idempotency_key,
|
|
688
|
-
writeback:
|
|
689
|
-
effects:
|
|
690
|
-
error: toActionRunFailure(row),
|
|
348
|
+
...(writeback === undefined ? {} : { writeback }),
|
|
349
|
+
...(effects === undefined ? {} : { effects }),
|
|
691
350
|
}
|
|
351
|
+
|
|
352
|
+
if (row.status === "succeeded") return { ...fields, status: "succeeded" }
|
|
353
|
+
return { ...fields, status: "failed", error: parseActionRunFailure(row.error) }
|
|
692
354
|
}
|
|
693
355
|
|
|
694
356
|
function rowToActionSubject(row: DatabaseRow): ActionSubject {
|
|
@@ -707,10 +369,6 @@ function rowToActionSubject(row: DatabaseRow): ActionSubject {
|
|
|
707
369
|
}
|
|
708
370
|
}
|
|
709
371
|
|
|
710
|
-
function isUniqueConstraintError(error: unknown): boolean {
|
|
711
|
-
return error instanceof Error && error.message.includes("UNIQUE constraint failed")
|
|
712
|
-
}
|
|
713
|
-
|
|
714
372
|
interface DatabaseRow {
|
|
715
373
|
project_id: string
|
|
716
374
|
id: string
|
|
@@ -719,11 +377,10 @@ interface DatabaseRow {
|
|
|
719
377
|
subject_kind: ActionSubject["kind"]
|
|
720
378
|
object_type_id: string | null
|
|
721
379
|
primary_id: string | null
|
|
722
|
-
status:
|
|
723
|
-
phase: ActionRunPhase
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
finished_at: string | null
|
|
380
|
+
status: ActionRunStatus
|
|
381
|
+
phase: ActionRunPhase
|
|
382
|
+
started_at: string
|
|
383
|
+
finished_at: string
|
|
727
384
|
params: string
|
|
728
385
|
idempotency_key: string
|
|
729
386
|
writeback_status: ActionRunWritebackRecord["status"] | null
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AgentStorage, ExecutionStorage } from "@sixb/core/storage"
|
|
2
|
-
import { installFreshSqliteSchema } from "../migrations"
|
|
3
2
|
import {
|
|
4
3
|
closeSqliteStoreConnection,
|
|
5
4
|
openSqliteStoreConnection,
|
|
@@ -35,10 +34,6 @@ export class SqliteAgentStorage implements AgentStorage {
|
|
|
35
34
|
constructor(options: SqliteAgentStorageOptions) {
|
|
36
35
|
this.connection = openSqliteStoreConnection(options)
|
|
37
36
|
|
|
38
|
-
if (this.connection.installFreshSchema) {
|
|
39
|
-
installFreshSqliteSchema(this.connection.db)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
37
|
this.threads = new SqliteAgentThreadStore(this.connection.db)
|
|
43
38
|
this.runs = new SqliteAgentRunStore(this.connection.db, options.executions)
|
|
44
39
|
this.messages = new SqliteAgentMessageStore(this.connection.db)
|