@promptbook/cli 0.113.0-5 → 0.113.0-8

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 (123) hide show
  1. package/apps/agents-server/src/app/admin/update/AdvancedOriginRepositoryPanel.tsx +76 -0
  2. package/apps/agents-server/src/app/admin/update/CurrentDeploymentCard.tsx +160 -0
  3. package/apps/agents-server/src/app/admin/update/PendingCommitsCard.tsx +86 -0
  4. package/apps/agents-server/src/app/admin/update/TargetEnvironmentCard.tsx +331 -0
  5. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +20 -873
  6. package/apps/agents-server/src/app/admin/update/UpdateJobCard.tsx +329 -0
  7. package/apps/agents-server/src/app/admin/update/UpdateOverview.ts +74 -0
  8. package/apps/agents-server/src/app/admin/update/buildDeploymentTimeBehindLabel.ts +29 -0
  9. package/apps/agents-server/src/app/admin/update/formatHumanReadableTimestamp.ts +18 -0
  10. package/apps/agents-server/src/app/admin/update/getUpdateJobFailureMessage.ts +13 -0
  11. package/apps/agents-server/src/app/admin/update/getUpdateJobSuccessMessage.ts +13 -0
  12. package/apps/agents-server/src/app/admin/update/useUpdateClientState.ts +441 -0
  13. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +13 -1
  14. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageClient.tsx +14 -559
  15. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageHeader.tsx +84 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageSection.tsx +52 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedCodePreview.tsx +138 -0
  18. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedHarnessSection.tsx +98 -0
  19. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/SourceBookSection.tsx +66 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeErrorBanner.tsx +30 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeExportWarningBanner.tsx +62 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/Transpiler.ts +9 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/useAgentCodeExportState.ts +478 -0
  24. package/apps/agents-server/src/app/system/utilities/mocked-chats/cloneMockedChatPreset.ts +17 -0
  25. package/apps/agents-server/src/app/system/utilities/mocked-chats/confirmDeleteMockedChat.ts +16 -0
  26. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedMessage.ts +81 -0
  27. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedParticipant.ts +146 -0
  28. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedSettings.ts +19 -0
  29. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDuplicatedDraft.ts +66 -0
  30. package/apps/agents-server/src/app/system/utilities/mocked-chats/findMockedChatById.ts +51 -0
  31. package/apps/agents-server/src/app/system/utilities/mocked-chats/persistMockedChats.ts +51 -0
  32. package/apps/agents-server/src/app/system/utilities/mocked-chats/useMockedChatsEditorState.ts +16 -426
  33. package/apps/agents-server/src/components/Homepage/buildFreeGraphBoxLayout.ts +446 -0
  34. package/apps/agents-server/src/components/Homepage/buildGraphLayoutNodes.ts +415 -157
  35. package/apps/agents-server/src/components/Homepage/useAgentsGraphCanvasState.ts +2 -1
  36. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +3 -1541
  37. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteQueryBuilder.ts +623 -0
  38. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteSupabaseClient.ts +64 -0
  39. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/ensureTable.ts +93 -0
  40. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteFilters.ts +202 -0
  41. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteMutationPayload.ts +131 -0
  42. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteRowOperations.ts +102 -0
  43. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteSql.ts +59 -0
  44. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTableSchema.ts +329 -0
  45. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTypes.ts +84 -0
  46. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteValueCodec.ts +63 -0
  47. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/normalizeSqliteError.ts +22 -0
  48. package/apps/agents-server/src/search/createDefaultServerSearchProviders/loadLocalOrganizationSearchDataset.ts +12 -1
  49. package/apps/agents-server/src/tools/send_email.ts +1 -2
  50. package/apps/agents-server/src/utils/agentOrganization/loadAgentOrganizationState.ts +13 -1
  51. package/apps/agents-server/src/utils/backup/createServerBackupZipStream.ts +7 -1382
  52. package/apps/agents-server/src/utils/backup/serverBackup/appendAgentBackupEntriesToZip.ts +27 -0
  53. package/apps/agents-server/src/utils/backup/serverBackup/appendConversationBackupEntriesToZip.ts +140 -0
  54. package/apps/agents-server/src/utils/backup/serverBackup/appendFileBackupEntriesToZip.ts +147 -0
  55. package/apps/agents-server/src/utils/backup/serverBackup/appendMessageBackupEntriesToZip.ts +65 -0
  56. package/apps/agents-server/src/utils/backup/serverBackup/appendMetadataBackupEntriesToZip.ts +39 -0
  57. package/apps/agents-server/src/utils/backup/serverBackup/appendSectionEntriesToZip.ts +92 -0
  58. package/apps/agents-server/src/utils/backup/serverBackup/appendUserBackupEntriesToZip.ts +80 -0
  59. package/apps/agents-server/src/utils/backup/serverBackup/createRedactedWalletBackupRecord.ts +34 -0
  60. package/apps/agents-server/src/utils/backup/serverBackup/createServerBackupManifest.ts +82 -0
  61. package/apps/agents-server/src/utils/backup/serverBackup/downloadBackupBinaryContent.ts +39 -0
  62. package/apps/agents-server/src/utils/backup/serverBackup/resolveFeedbackThreadMessages.ts +53 -0
  63. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupAttachments.ts +151 -0
  64. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupContext.ts +167 -0
  65. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupFilenames.ts +161 -0
  66. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupPreviews.ts +97 -0
  67. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupRowUtilities.ts +158 -0
  68. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupTypes.ts +59 -0
  69. package/apps/agents-server/src/utils/createLocalAgentSourceImporter.ts +159 -0
  70. package/apps/agents-server/src/utils/createMissingImportedAgentFallback.ts +60 -0
  71. package/apps/agents-server/src/utils/customDomainRouting.ts +157 -12
  72. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +1 -1
  73. package/apps/agents-server/src/utils/importAgentWithFallback.ts +1 -58
  74. package/apps/agents-server/src/utils/localAgentRouteReferences.ts +167 -0
  75. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +1 -1
  76. package/apps/agents-server/src/utils/managementApi/managementApiAgents.ts +17 -3
  77. package/apps/agents-server/src/utils/messages/sendMessage.ts +5 -3
  78. package/apps/agents-server/src/utils/resolveAgentStateFromSource.ts +7 -1
  79. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +54 -5
  80. package/apps/agents-server/src/utils/resolveServerAgentContext.ts +12 -1
  81. package/apps/agents-server/src/utils/resolveStoredAgentState.ts +2 -1
  82. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition/SpeechToTextFailoverRecognitionProviderRuntime.ts +0 -1
  83. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition.ts +34 -2
  84. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +39 -9
  85. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +5 -0
  86. package/esm/index.es.js +96 -57
  87. package/esm/index.es.js.map +1 -1
  88. package/esm/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  89. package/esm/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  90. package/esm/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  91. package/esm/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  92. package/esm/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  93. package/esm/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  94. package/esm/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  95. package/esm/src/version.d.ts +1 -1
  96. package/package.json +1 -1
  97. package/src/avatars/avatarAnimationScheduler.ts +2 -1
  98. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +6 -2
  99. package/src/avatars/visuals/octopus3d4AvatarVisual.ts +14 -18
  100. package/src/book-components/Chat/Chat/insertDictationChunk.ts +3 -3
  101. package/src/book-components/Chat/Chat/learnDictationDictionary.ts +1 -1
  102. package/src/book-components/Chat/Chat/useChatInputAreaDictation.ts +133 -26
  103. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +3 -1
  104. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +14 -21
  105. package/src/cli/cli-commands/coder/find-refactor-candidates.ts +9 -2
  106. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  107. package/src/cli/cli-commands/coder/run.ts +3 -27
  108. package/src/cli/cli-commands/common/createPositiveIntegerOptionParser.ts +31 -0
  109. package/src/commitments/TEAM/TEAM.ts +5 -5
  110. package/src/other/templates/getTemplatesPipelineCollection.ts +727 -781
  111. package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +1 -4
  112. package/src/version.ts +2 -2
  113. package/src/versions.txt +1 -0
  114. package/umd/index.umd.js +96 -57
  115. package/umd/index.umd.js.map +1 -1
  116. package/umd/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  117. package/umd/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  118. package/umd/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  119. package/umd/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  120. package/umd/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  121. package/umd/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  122. package/umd/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  123. package/umd/src/version.d.ts +1 -1
@@ -0,0 +1,623 @@
1
+ import type { TODO_any } from '@promptbook-local/types';
2
+ import type { AgentsServerSqliteDatabase } from '../$provideAgentsServerSqliteDatabase';
3
+ import type {
4
+ LocalSqliteFilter,
5
+ LocalSqliteOperation,
6
+ LocalSqliteOrder,
7
+ LocalSqliteQueryResult,
8
+ LocalSqliteSelectOptions,
9
+ LocalSqliteSqlFragment,
10
+ LocalSqliteUpsertOptions,
11
+ } from './localSqliteTypes';
12
+ import { ensureTable } from './ensureTable';
13
+ import {
14
+ createFilterCondition,
15
+ createOrFilterCondition,
16
+ parsePostgrestFilter,
17
+ splitPostgrestOrFilter,
18
+ type ParsedPostgrestFilter,
19
+ } from './localSqliteFilters';
20
+ import { normalizeMutationRows, stripUndefinedValues, withInsertDefaults } from './localSqliteMutationPayload';
21
+ import { findConflictRowid, insertRow, selectRowsByRowids, updateRowid } from './localSqliteRowOperations';
22
+ import { createSelectExpression, parseSelectedColumns, quoteIdentifier } from './localSqliteSql';
23
+ import { resolveTableBaseName, resolveUpsertConflictColumns } from './localSqliteTableSchema';
24
+ import { deserializeRow, serializeValue } from './localSqliteValueCodec';
25
+ import { normalizeSqliteError } from './normalizeSqliteError';
26
+
27
+ /**
28
+ * Supabase-shaped thenable query builder executed by `await`.
29
+ *
30
+ * @private class of `$provideLocalSqliteSupabase`
31
+ */
32
+ export class LocalSqliteQueryBuilder implements PromiseLike<LocalSqliteQueryResult> {
33
+ private operation: LocalSqliteOperation = 'select';
34
+ private selectedColumns = '*';
35
+ private selectOptions: LocalSqliteSelectOptions = {};
36
+ private filters: Array<LocalSqliteFilter> = [];
37
+ private orFilters: Array<string> = [];
38
+ private orders: Array<LocalSqliteOrder> = [];
39
+ private limitCount: number | null = null;
40
+ private offsetCount: number | null = null;
41
+ private singleMode: 'single' | 'maybeSingle' | null = null;
42
+ private mutationRows: Array<Record<string, unknown>> = [];
43
+ private mutationValues: Record<string, unknown> = {};
44
+ private upsertOptions: LocalSqliteUpsertOptions = {};
45
+ private signal: AbortSignal | null = null;
46
+ private isReturningSelection = false;
47
+
48
+ public constructor(private readonly database: AgentsServerSqliteDatabase, private readonly tableName: string) {}
49
+
50
+ /**
51
+ * Configures selected columns or mutation return columns.
52
+ */
53
+ public select(columns = '*', options: LocalSqliteSelectOptions = {}): this {
54
+ if (this.operation !== 'select') {
55
+ this.isReturningSelection = true;
56
+ }
57
+
58
+ this.selectedColumns = columns || '*';
59
+ this.selectOptions = options;
60
+ return this;
61
+ }
62
+
63
+ /**
64
+ * Configures inserted rows.
65
+ */
66
+ public insert(values: TODO_any): this {
67
+ this.operation = 'insert';
68
+ this.mutationRows = normalizeMutationRows(values);
69
+ return this;
70
+ }
71
+
72
+ /**
73
+ * Configures updated values.
74
+ */
75
+ public update(values: Record<string, unknown>): this {
76
+ this.operation = 'update';
77
+ this.mutationValues = stripUndefinedValues(values);
78
+ return this;
79
+ }
80
+
81
+ /**
82
+ * Configures row deletion.
83
+ */
84
+ public delete(): this {
85
+ this.operation = 'delete';
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Configures inserted-or-updated rows.
91
+ */
92
+ public upsert(values: TODO_any, options: LocalSqliteUpsertOptions = {}): this {
93
+ this.operation = 'upsert';
94
+ this.mutationRows = normalizeMutationRows(values);
95
+ this.upsertOptions = options;
96
+ return this;
97
+ }
98
+
99
+ /**
100
+ * Adds equality filter.
101
+ */
102
+ public eq(column: string, value: unknown): this {
103
+ this.filters.push({ column, operator: 'eq', value });
104
+ return this;
105
+ }
106
+
107
+ /**
108
+ * Adds inequality filter.
109
+ */
110
+ public neq(column: string, value: unknown): this {
111
+ this.filters.push({ column, operator: 'neq', value });
112
+ return this;
113
+ }
114
+
115
+ /**
116
+ * Adds nullability filter.
117
+ */
118
+ public is(column: string, value: unknown): this {
119
+ this.filters.push({ column, operator: 'is', value });
120
+ return this;
121
+ }
122
+
123
+ /**
124
+ * Adds negative filter for supported operators.
125
+ */
126
+ public not(column: string, operator: string, value: unknown): this {
127
+ if (operator === 'is') {
128
+ this.filters.push({ column, operator: 'not-is', value });
129
+ } else if (operator === 'eq') {
130
+ this.filters.push({ column, operator: 'neq', value });
131
+ }
132
+ return this;
133
+ }
134
+
135
+ /**
136
+ * Adds `IN` filter.
137
+ */
138
+ public in(column: string, value: ReadonlyArray<unknown>): this {
139
+ this.filters.push({ column, operator: 'in', value });
140
+ return this;
141
+ }
142
+
143
+ /**
144
+ * Adds less-than filter.
145
+ */
146
+ public lt(column: string, value: unknown): this {
147
+ this.filters.push({ column, operator: 'lt', value });
148
+ return this;
149
+ }
150
+
151
+ /**
152
+ * Adds less-than-or-equal filter.
153
+ */
154
+ public lte(column: string, value: unknown): this {
155
+ this.filters.push({ column, operator: 'lte', value });
156
+ return this;
157
+ }
158
+
159
+ /**
160
+ * Adds greater-than filter.
161
+ */
162
+ public gt(column: string, value: unknown): this {
163
+ this.filters.push({ column, operator: 'gt', value });
164
+ return this;
165
+ }
166
+
167
+ /**
168
+ * Adds greater-than-or-equal filter.
169
+ */
170
+ public gte(column: string, value: unknown): this {
171
+ this.filters.push({ column, operator: 'gte', value });
172
+ return this;
173
+ }
174
+
175
+ /**
176
+ * Adds SQL LIKE filter.
177
+ */
178
+ public like(column: string, value: string): this {
179
+ this.filters.push({ column, operator: 'like', value });
180
+ return this;
181
+ }
182
+
183
+ /**
184
+ * Adds case-insensitive LIKE filter.
185
+ */
186
+ public ilike(column: string, value: string): this {
187
+ this.filters.push({ column, operator: 'ilike', value });
188
+ return this;
189
+ }
190
+
191
+ /**
192
+ * Adds an OR filter in the PostgREST format used by Supabase.
193
+ */
194
+ public or(filter: string): this {
195
+ this.orFilters.push(filter);
196
+ return this;
197
+ }
198
+
199
+ /**
200
+ * Adds ordering.
201
+ */
202
+ public order(column: string, options: { ascending?: boolean; nullsFirst?: boolean } = {}): this {
203
+ this.orders.push({
204
+ column,
205
+ ascending: options.ascending !== false,
206
+ nullsFirst: options.nullsFirst,
207
+ });
208
+ return this;
209
+ }
210
+
211
+ /**
212
+ * Adds a limit.
213
+ */
214
+ public limit(count: number): this {
215
+ this.limitCount = count;
216
+ return this;
217
+ }
218
+
219
+ /**
220
+ * Adds inclusive range pagination.
221
+ */
222
+ public range(from: number, to: number): this {
223
+ this.offsetCount = from;
224
+ this.limitCount = Math.max(0, to - from + 1);
225
+ return this;
226
+ }
227
+
228
+ /**
229
+ * Marks the query as requiring exactly one row.
230
+ */
231
+ public single(): Promise<LocalSqliteQueryResult> {
232
+ this.singleMode = 'single';
233
+ return this.execute();
234
+ }
235
+
236
+ /**
237
+ * Marks the query as requiring at most one row.
238
+ */
239
+ public maybeSingle(): Promise<LocalSqliteQueryResult> {
240
+ this.singleMode = 'maybeSingle';
241
+ return this.execute();
242
+ }
243
+
244
+ /**
245
+ * Accepts an abort signal for API compatibility.
246
+ */
247
+ public abortSignal(signal: AbortSignal): this {
248
+ this.signal = signal;
249
+ return this;
250
+ }
251
+
252
+ /**
253
+ * Makes the query builder awaitable.
254
+ */
255
+ public then<TResult1 = LocalSqliteQueryResult, TResult2 = never>(
256
+ onfulfilled?: ((value: LocalSqliteQueryResult) => TResult1 | PromiseLike<TResult1>) | null,
257
+ onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
258
+ ): Promise<TResult1 | TResult2> {
259
+ return this.execute().then(onfulfilled, onrejected);
260
+ }
261
+
262
+ /**
263
+ * Executes the configured query.
264
+ *
265
+ * @private method of `LocalSqliteQueryBuilder`
266
+ */
267
+ private async execute(): Promise<LocalSqliteQueryResult> {
268
+ try {
269
+ if (this.signal?.aborted) {
270
+ throw new Error('The operation was aborted.');
271
+ }
272
+
273
+ switch (this.operation) {
274
+ case 'insert':
275
+ return this.executeInsert();
276
+ case 'update':
277
+ return this.executeUpdate();
278
+ case 'delete':
279
+ return this.executeDelete();
280
+ case 'upsert':
281
+ return this.executeUpsert();
282
+ case 'select':
283
+ default:
284
+ return this.executeSelect();
285
+ }
286
+ } catch (error) {
287
+ return {
288
+ data: null,
289
+ error: normalizeSqliteError(error),
290
+ status: 400,
291
+ statusText: 'Bad Request',
292
+ };
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Executes a select query.
298
+ *
299
+ * @private method of `LocalSqliteQueryBuilder`
300
+ */
301
+ private executeSelect(): LocalSqliteQueryResult {
302
+ const selectedColumns = parseSelectedColumns(this.selectedColumns);
303
+ const requiredColumns = [
304
+ ...selectedColumns,
305
+ ...this.filters.map((filter) => filter.column),
306
+ ...this.orders.map((order) => order.column),
307
+ ...this.extractOrFilterColumns(),
308
+ ];
309
+ ensureTable(this.database, this.tableName, requiredColumns);
310
+
311
+ const where = this.createWhereClause();
312
+ const orderBy = this.createOrderByClause();
313
+ const limit = this.createLimitClause();
314
+ const count = this.selectOptions.count === 'exact' ? this.executeCount(where) : null;
315
+
316
+ if (this.selectOptions.head) {
317
+ return {
318
+ data: null,
319
+ error: null,
320
+ count,
321
+ status: 200,
322
+ statusText: 'OK',
323
+ };
324
+ }
325
+
326
+ const sql = [
327
+ `SELECT ${createSelectExpression(selectedColumns)} FROM ${quoteIdentifier(this.tableName)}`,
328
+ where.sql,
329
+ orderBy,
330
+ limit.sql,
331
+ ]
332
+ .filter(Boolean)
333
+ .join(' ');
334
+ const rows = this.database.prepare(sql).all(...where.values, ...limit.values);
335
+ const data = rows.map((row) => deserializeRow(this.tableName, row));
336
+
337
+ return this.finalizeDataResponse(data, count);
338
+ }
339
+
340
+ /**
341
+ * Executes an insert query.
342
+ *
343
+ * @private method of `LocalSqliteQueryBuilder`
344
+ */
345
+ private executeInsert(): LocalSqliteQueryResult {
346
+ const insertedRowids: Array<number | bigint> = [];
347
+
348
+ for (const rawRow of this.mutationRows) {
349
+ const row = withInsertDefaults(resolveTableBaseName(this.tableName), rawRow);
350
+ ensureTable(this.database, this.tableName, Object.keys(row));
351
+ insertedRowids.push(insertRow(this.database, this.tableName, row).lastInsertRowid);
352
+ }
353
+
354
+ return this.createMutationResponse(insertedRowids);
355
+ }
356
+
357
+ /**
358
+ * Executes an update query.
359
+ *
360
+ * @private method of `LocalSqliteQueryBuilder`
361
+ */
362
+ private executeUpdate(): LocalSqliteQueryResult {
363
+ const updateColumns = Object.keys(this.mutationValues);
364
+ ensureTable(this.database, this.tableName, [
365
+ ...updateColumns,
366
+ ...this.filters.map((filter) => filter.column),
367
+ ...this.extractOrFilterColumns(),
368
+ ]);
369
+
370
+ const rowids = this.selectMatchingRowids();
371
+
372
+ if (rowids.length > 0 && updateColumns.length > 0) {
373
+ const assignments = updateColumns.map((column) => `${quoteIdentifier(column)} = ?`).join(', ');
374
+ const values = updateColumns.map((column) =>
375
+ serializeValue(this.tableName, column, this.mutationValues[column]),
376
+ );
377
+ const rowidPlaceholders = rowids.map(() => '?').join(', ');
378
+ this.database
379
+ .prepare(
380
+ `UPDATE ${quoteIdentifier(
381
+ this.tableName,
382
+ )} SET ${assignments} WHERE rowid IN (${rowidPlaceholders})`,
383
+ )
384
+ .run(...values, ...rowids);
385
+ }
386
+
387
+ return this.createMutationResponse(rowids);
388
+ }
389
+
390
+ /**
391
+ * Executes a delete query.
392
+ *
393
+ * @private method of `LocalSqliteQueryBuilder`
394
+ */
395
+ private executeDelete(): LocalSqliteQueryResult {
396
+ ensureTable(this.database, this.tableName, [
397
+ ...this.filters.map((filter) => filter.column),
398
+ ...this.extractOrFilterColumns(),
399
+ ]);
400
+
401
+ const rowids = this.selectMatchingRowids();
402
+ if (rowids.length > 0) {
403
+ const rowidPlaceholders = rowids.map(() => '?').join(', ');
404
+ this.database
405
+ .prepare(`DELETE FROM ${quoteIdentifier(this.tableName)} WHERE rowid IN (${rowidPlaceholders})`)
406
+ .run(...rowids);
407
+ }
408
+
409
+ return this.createMutationResponse([]);
410
+ }
411
+
412
+ /**
413
+ * Executes an upsert query.
414
+ *
415
+ * @private method of `LocalSqliteQueryBuilder`
416
+ */
417
+ private executeUpsert(): LocalSqliteQueryResult {
418
+ const affectedRowids: Array<number | bigint> = [];
419
+ const tableBaseName = resolveTableBaseName(this.tableName);
420
+ const conflictColumns = resolveUpsertConflictColumns(tableBaseName, this.upsertOptions);
421
+
422
+ for (const rawRow of this.mutationRows) {
423
+ const row = withInsertDefaults(tableBaseName, rawRow);
424
+ ensureTable(this.database, this.tableName, [...Object.keys(row), ...conflictColumns]);
425
+ const existingRowid =
426
+ conflictColumns.length > 0
427
+ ? findConflictRowid(this.database, this.tableName, row, conflictColumns)
428
+ : null;
429
+
430
+ if (existingRowid !== null) {
431
+ updateRowid(this.database, this.tableName, existingRowid, row);
432
+ affectedRowids.push(existingRowid);
433
+ } else {
434
+ affectedRowids.push(insertRow(this.database, this.tableName, row).lastInsertRowid);
435
+ }
436
+ }
437
+
438
+ return this.createMutationResponse(affectedRowids);
439
+ }
440
+
441
+ /**
442
+ * Creates a mutation response, optionally loading selected mutated rows.
443
+ *
444
+ * @private method of `LocalSqliteQueryBuilder`
445
+ */
446
+ private createMutationResponse(rowids: ReadonlyArray<number | bigint>): LocalSqliteQueryResult {
447
+ if (!this.isReturningSelection) {
448
+ return {
449
+ data: null,
450
+ error: null,
451
+ status: 201,
452
+ statusText: 'Created',
453
+ };
454
+ }
455
+
456
+ const data = selectRowsByRowids(
457
+ this.database,
458
+ this.tableName,
459
+ rowids,
460
+ parseSelectedColumns(this.selectedColumns),
461
+ );
462
+ return this.finalizeDataResponse(data, null);
463
+ }
464
+
465
+ /**
466
+ * Applies single/maybeSingle response semantics.
467
+ *
468
+ * @private method of `LocalSqliteQueryBuilder`
469
+ */
470
+ private finalizeDataResponse(data: Array<Record<string, unknown>>, count: number | null): LocalSqliteQueryResult {
471
+ if (this.singleMode === 'single') {
472
+ if (data.length !== 1) {
473
+ return {
474
+ data: null,
475
+ error: {
476
+ code: 'PGRST116',
477
+ message: `Expected exactly one row, received ${data.length}.`,
478
+ },
479
+ count,
480
+ status: 406,
481
+ statusText: 'Not Acceptable',
482
+ };
483
+ }
484
+
485
+ return { data: data[0], error: null, count, status: 200, statusText: 'OK' };
486
+ }
487
+
488
+ if (this.singleMode === 'maybeSingle') {
489
+ if (data.length > 1) {
490
+ return {
491
+ data: null,
492
+ error: {
493
+ code: 'PGRST116',
494
+ message: `Expected zero or one row, received ${data.length}.`,
495
+ },
496
+ count,
497
+ status: 406,
498
+ statusText: 'Not Acceptable',
499
+ };
500
+ }
501
+
502
+ return { data: data[0] || null, error: null, count, status: 200, statusText: 'OK' };
503
+ }
504
+
505
+ return { data, error: null, count, status: 200, statusText: 'OK' };
506
+ }
507
+
508
+ /**
509
+ * Counts rows matching the current filters.
510
+ *
511
+ * @private method of `LocalSqliteQueryBuilder`
512
+ */
513
+ private executeCount(where: LocalSqliteSqlFragment): number {
514
+ const row = this.database
515
+ .prepare(`SELECT COUNT(*) AS "count" FROM ${quoteIdentifier(this.tableName)} ${where.sql}`)
516
+ .get(...where.values);
517
+
518
+ return Number(row?.count || 0);
519
+ }
520
+
521
+ /**
522
+ * Selects matching SQLite rowids before a mutation changes filtered columns.
523
+ *
524
+ * @private method of `LocalSqliteQueryBuilder`
525
+ */
526
+ private selectMatchingRowids(): Array<number | bigint> {
527
+ const where = this.createWhereClause();
528
+ const sql = `SELECT rowid AS "__rowid" FROM ${quoteIdentifier(this.tableName)} ${where.sql}`;
529
+ return this.database
530
+ .prepare(sql)
531
+ .all(...where.values)
532
+ .map((row) => row.__rowid as number | bigint);
533
+ }
534
+
535
+ /**
536
+ * Creates the SQL WHERE clause.
537
+ *
538
+ * @private method of `LocalSqliteQueryBuilder`
539
+ */
540
+ private createWhereClause(): LocalSqliteSqlFragment {
541
+ const parts: Array<string> = [];
542
+ const values: Array<unknown> = [];
543
+
544
+ for (const filter of this.filters) {
545
+ const condition = createFilterCondition(this.tableName, filter);
546
+ parts.push(condition.sql);
547
+ values.push(...condition.values);
548
+ }
549
+
550
+ for (const filter of this.orFilters) {
551
+ const condition = createOrFilterCondition(this.tableName, filter);
552
+ if (!condition) {
553
+ continue;
554
+ }
555
+
556
+ parts.push(condition.sql);
557
+ values.push(...condition.values);
558
+ }
559
+
560
+ return {
561
+ sql: parts.length > 0 ? `WHERE ${parts.join(' AND ')}` : '',
562
+ values,
563
+ };
564
+ }
565
+
566
+ /**
567
+ * Creates the SQL ORDER BY clause.
568
+ *
569
+ * @private method of `LocalSqliteQueryBuilder`
570
+ */
571
+ private createOrderByClause(): string {
572
+ if (this.orders.length === 0) {
573
+ return '';
574
+ }
575
+
576
+ const orderParts: Array<string> = [];
577
+ for (const order of this.orders) {
578
+ const quotedColumn = quoteIdentifier(order.column);
579
+ const direction = order.ascending ? 'ASC' : 'DESC';
580
+
581
+ if (order.nullsFirst === true) {
582
+ orderParts.push(`${quotedColumn} IS NOT NULL ASC`);
583
+ } else if (order.nullsFirst === false) {
584
+ orderParts.push(`${quotedColumn} IS NULL ASC`);
585
+ }
586
+
587
+ orderParts.push(`${quotedColumn} ${direction}`);
588
+ }
589
+
590
+ return `ORDER BY ${orderParts.join(', ')}`;
591
+ }
592
+
593
+ /**
594
+ * Creates the SQL LIMIT/OFFSET clause.
595
+ *
596
+ * @private method of `LocalSqliteQueryBuilder`
597
+ */
598
+ private createLimitClause(): LocalSqliteSqlFragment {
599
+ if (this.limitCount === null) {
600
+ return { sql: '', values: [] };
601
+ }
602
+
603
+ if (this.offsetCount === null) {
604
+ return { sql: 'LIMIT ?', values: [this.limitCount] };
605
+ }
606
+
607
+ return { sql: 'LIMIT ? OFFSET ?', values: [this.limitCount, this.offsetCount] };
608
+ }
609
+
610
+ /**
611
+ * Extracts column names referenced in OR filters.
612
+ *
613
+ * @private method of `LocalSqliteQueryBuilder`
614
+ */
615
+ private extractOrFilterColumns(): Array<string> {
616
+ return this.orFilters.flatMap((filter) =>
617
+ splitPostgrestOrFilter(filter)
618
+ .map(parsePostgrestFilter)
619
+ .filter((parsedFilter): parsedFilter is ParsedPostgrestFilter => parsedFilter !== null)
620
+ .map((parsedFilter) => parsedFilter.column),
621
+ );
622
+ }
623
+ }
@@ -0,0 +1,64 @@
1
+ import type { TODO_any } from '@promptbook-local/types';
2
+ import type { AgentsServerSqliteDatabase } from '../$provideAgentsServerSqliteDatabase';
3
+ import type { LocalSqliteSelectOptions, LocalSqliteUpsertOptions } from './localSqliteTypes';
4
+ import { LocalSqliteQueryBuilder } from './LocalSqliteQueryBuilder';
5
+
6
+ /**
7
+ * Supabase-shaped client with only the table query surface used by Agents Server.
8
+ *
9
+ * @private class of `$provideLocalSqliteSupabase`
10
+ */
11
+ export class LocalSqliteSupabaseClient {
12
+ public constructor(private readonly database: AgentsServerSqliteDatabase) {}
13
+
14
+ /**
15
+ * Starts a query for one SQLite table.
16
+ */
17
+ public from(tableName: string): LocalSqliteTable {
18
+ return new LocalSqliteTable(this.database, tableName);
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Supabase-shaped table entry point. Every operation starts a fresh query builder.
24
+ *
25
+ * @private class of `LocalSqliteSupabaseClient`
26
+ */
27
+ class LocalSqliteTable {
28
+ public constructor(private readonly database: AgentsServerSqliteDatabase, private readonly tableName: string) {}
29
+
30
+ /**
31
+ * Starts a select query.
32
+ */
33
+ public select(columns = '*', options?: LocalSqliteSelectOptions): LocalSqliteQueryBuilder {
34
+ return new LocalSqliteQueryBuilder(this.database, this.tableName).select(columns, options);
35
+ }
36
+
37
+ /**
38
+ * Starts an insert query.
39
+ */
40
+ public insert(values: TODO_any): LocalSqliteQueryBuilder {
41
+ return new LocalSqliteQueryBuilder(this.database, this.tableName).insert(values);
42
+ }
43
+
44
+ /**
45
+ * Starts an update query.
46
+ */
47
+ public update(values: Record<string, unknown>): LocalSqliteQueryBuilder {
48
+ return new LocalSqliteQueryBuilder(this.database, this.tableName).update(values);
49
+ }
50
+
51
+ /**
52
+ * Starts a delete query.
53
+ */
54
+ public delete(): LocalSqliteQueryBuilder {
55
+ return new LocalSqliteQueryBuilder(this.database, this.tableName).delete();
56
+ }
57
+
58
+ /**
59
+ * Starts an upsert query.
60
+ */
61
+ public upsert(values: TODO_any, options?: LocalSqliteUpsertOptions): LocalSqliteQueryBuilder {
62
+ return new LocalSqliteQueryBuilder(this.database, this.tableName).upsert(values, options);
63
+ }
64
+ }