@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,93 @@
1
+ import type { AgentsServerSqliteDatabase } from '../$provideAgentsServerSqliteDatabase';
2
+ import { quoteIdentifier, sanitizeSqlIdentifier, uniqueStrings } from './localSqliteSql';
3
+ import {
4
+ isTextPrimaryKeyTable,
5
+ resolveReadIndexColumns,
6
+ resolveReadIndexes,
7
+ resolveSqliteColumnType,
8
+ resolveTableBaseName,
9
+ resolveUniqueIndexColumns,
10
+ resolveUniqueIndexes,
11
+ } from './localSqliteTableSchema';
12
+
13
+ /**
14
+ * Ensures a table and all required columns exist.
15
+ *
16
+ * @private function of `$provideLocalSqliteSupabase`
17
+ */
18
+ export function ensureTable(
19
+ database: AgentsServerSqliteDatabase,
20
+ tableName: string,
21
+ requiredColumns: ReadonlyArray<string>,
22
+ ): void {
23
+ const tableBaseName = resolveTableBaseName(tableName);
24
+ const primaryKey = isTextPrimaryKeyTable(tableBaseName)
25
+ ? '"id" TEXT PRIMARY KEY'
26
+ : '"id" INTEGER PRIMARY KEY AUTOINCREMENT';
27
+
28
+ database.exec(`CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (${primaryKey})`);
29
+
30
+ const existingColumns = new Set(
31
+ database
32
+ .prepare(`PRAGMA table_info(${quoteIdentifier(tableName)})`)
33
+ .all()
34
+ .map((row) => String(row.name)),
35
+ );
36
+ const columnsToEnsure = uniqueStrings([...requiredColumns, ...resolveUniqueIndexColumns(tableBaseName)]).filter(
37
+ (column) => column !== '*' && column !== 'id',
38
+ );
39
+ const columnsToEnsureWithIndexes = uniqueStrings([...columnsToEnsure, ...resolveReadIndexColumns(tableBaseName)]);
40
+
41
+ for (const column of columnsToEnsureWithIndexes) {
42
+ if (existingColumns.has(column)) {
43
+ continue;
44
+ }
45
+
46
+ database.exec(
47
+ `ALTER TABLE ${quoteIdentifier(tableName)} ADD COLUMN ${quoteIdentifier(column)} ${resolveSqliteColumnType(
48
+ tableBaseName,
49
+ column,
50
+ )}`,
51
+ );
52
+ existingColumns.add(column);
53
+ }
54
+
55
+ ensureUniqueIndexes(database, tableName, tableBaseName);
56
+ ensureReadIndexes(database, tableName, tableBaseName);
57
+ }
58
+
59
+ /**
60
+ * Creates known unique indexes after required columns exist.
61
+ *
62
+ * @private function of `ensureTable`
63
+ */
64
+ function ensureUniqueIndexes(database: AgentsServerSqliteDatabase, tableName: string, tableBaseName: string): void {
65
+ const uniqueIndexes = resolveUniqueIndexes(tableBaseName);
66
+
67
+ for (const columns of uniqueIndexes) {
68
+ const indexName = `idx_${sanitizeSqlIdentifier(tableName)}_${columns.join('_')}_unique`;
69
+ const columnSql = columns.map(quoteIdentifier).join(', ');
70
+ database.exec(
71
+ `CREATE UNIQUE INDEX IF NOT EXISTS ${quoteIdentifier(indexName)} ON ${quoteIdentifier(
72
+ tableName,
73
+ )} (${columnSql})`,
74
+ );
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Creates known read indexes after required columns exist.
80
+ *
81
+ * @private function of `ensureTable`
82
+ */
83
+ function ensureReadIndexes(database: AgentsServerSqliteDatabase, tableName: string, tableBaseName: string): void {
84
+ const readIndexes = resolveReadIndexes(tableBaseName);
85
+
86
+ for (const readIndex of readIndexes) {
87
+ const indexName = `index_${sanitizeSqlIdentifier(tableName)}_${sanitizeSqlIdentifier(readIndex.name)}`;
88
+ const columnSql = readIndex.columns.map(quoteIdentifier).join(', ');
89
+ database.exec(
90
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)} (${columnSql})`,
91
+ );
92
+ }
93
+ }
@@ -0,0 +1,202 @@
1
+ import type { LocalSqliteFilter, LocalSqliteSqlFragment } from './localSqliteTypes';
2
+ import { quoteIdentifier } from './localSqliteSql';
3
+ import { serializeValue } from './localSqliteValueCodec';
4
+
5
+ /**
6
+ * Parsed PostgREST filter expression.
7
+ *
8
+ * @private type of `$provideLocalSqliteSupabase`
9
+ */
10
+ export type ParsedPostgrestFilter = {
11
+ readonly column: string;
12
+ readonly operator: string;
13
+ readonly value: string;
14
+ };
15
+
16
+ /**
17
+ * Creates SQL for one simple filter.
18
+ *
19
+ * @private function of `$provideLocalSqliteSupabase`
20
+ */
21
+ export function createFilterCondition(tableName: string, filter: LocalSqliteFilter): LocalSqliteSqlFragment {
22
+ const column = quoteIdentifier(filter.column);
23
+ const value = serializeValue(tableName, filter.column, filter.value);
24
+
25
+ switch (filter.operator) {
26
+ case 'eq':
27
+ return value === null
28
+ ? { sql: `${column} IS NULL`, values: [] }
29
+ : { sql: `${column} = ?`, values: [value] };
30
+ case 'neq':
31
+ return value === null
32
+ ? { sql: `${column} IS NOT NULL`, values: [] }
33
+ : { sql: `${column} <> ?`, values: [value] };
34
+ case 'is':
35
+ return filter.value === null
36
+ ? { sql: `${column} IS NULL`, values: [] }
37
+ : { sql: `${column} IS ?`, values: [value] };
38
+ case 'not-is':
39
+ return filter.value === null
40
+ ? { sql: `${column} IS NOT NULL`, values: [] }
41
+ : { sql: `${column} IS NOT ?`, values: [value] };
42
+ case 'in': {
43
+ const values = Array.isArray(filter.value)
44
+ ? filter.value.map((item) => serializeValue(tableName, filter.column, item))
45
+ : [];
46
+ if (values.length === 0) {
47
+ return { sql: '0 = 1', values: [] };
48
+ }
49
+ return { sql: `${column} IN (${values.map(() => '?').join(', ')})`, values };
50
+ }
51
+ case 'lt':
52
+ return { sql: `${column} < ?`, values: [value] };
53
+ case 'lte':
54
+ return { sql: `${column} <= ?`, values: [value] };
55
+ case 'gt':
56
+ return { sql: `${column} > ?`, values: [value] };
57
+ case 'gte':
58
+ return { sql: `${column} >= ?`, values: [value] };
59
+ case 'like':
60
+ return { sql: `${column} LIKE ? ESCAPE '\\'`, values: [value] };
61
+ case 'ilike':
62
+ return { sql: `LOWER(${column}) LIKE LOWER(?) ESCAPE '\\'`, values: [value] };
63
+ default:
64
+ return { sql: '1 = 1', values: [] };
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Creates SQL for one PostgREST `.or(...)` filter.
70
+ *
71
+ * @private function of `$provideLocalSqliteSupabase`
72
+ */
73
+ export function createOrFilterCondition(tableName: string, filter: string): LocalSqliteSqlFragment | null {
74
+ const conditions: Array<string> = [];
75
+ const values: Array<unknown> = [];
76
+
77
+ for (const part of splitPostgrestOrFilter(filter)) {
78
+ const parsedFilter = parsePostgrestFilter(part);
79
+ if (!parsedFilter) {
80
+ continue;
81
+ }
82
+
83
+ if (parsedFilter.operator === 'cs') {
84
+ conditions.push('0 = 1');
85
+ continue;
86
+ }
87
+
88
+ const condition = createFilterCondition(tableName, {
89
+ column: parsedFilter.column,
90
+ operator: normalizePostgrestOperator(parsedFilter.operator),
91
+ value: decodePostgrestFilterValue(parsedFilter.value),
92
+ });
93
+ conditions.push(condition.sql);
94
+ values.push(...condition.values);
95
+ }
96
+
97
+ if (conditions.length === 0) {
98
+ return null;
99
+ }
100
+
101
+ return {
102
+ sql: `(${conditions.join(' OR ')})`,
103
+ values,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Splits a PostgREST OR filter while keeping JSON literals intact.
109
+ *
110
+ * @private function of `$provideLocalSqliteSupabase`
111
+ */
112
+ export function splitPostgrestOrFilter(filter: string): Array<string> {
113
+ const parts: Array<string> = [];
114
+ let current = '';
115
+ let depth = 0;
116
+ let isInsideString = false;
117
+
118
+ for (let index = 0; index < filter.length; index++) {
119
+ const character = filter[index]!;
120
+ const previousCharacter = filter[index - 1];
121
+
122
+ if (character === '"' && previousCharacter !== '\\') {
123
+ isInsideString = !isInsideString;
124
+ } else if (!isInsideString && (character === '{' || character === '[')) {
125
+ depth++;
126
+ } else if (!isInsideString && (character === '}' || character === ']')) {
127
+ depth--;
128
+ }
129
+
130
+ if (character === ',' && depth === 0 && !isInsideString) {
131
+ parts.push(current);
132
+ current = '';
133
+ continue;
134
+ }
135
+
136
+ current += character;
137
+ }
138
+
139
+ if (current) {
140
+ parts.push(current);
141
+ }
142
+
143
+ return parts.map((part) => part.trim()).filter(Boolean);
144
+ }
145
+
146
+ /**
147
+ * Parses one PostgREST filter expression.
148
+ *
149
+ * @private function of `$provideLocalSqliteSupabase`
150
+ */
151
+ export function parsePostgrestFilter(filter: string): ParsedPostgrestFilter | null {
152
+ const match = /^([^.]*)\.([a-z]+)\.([\s\S]*)$/iu.exec(filter.trim());
153
+ if (!match) {
154
+ return null;
155
+ }
156
+
157
+ return {
158
+ column: match[1]!,
159
+ operator: match[2]!.toLowerCase(),
160
+ value: match[3]!,
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Converts PostgREST operators into internal filter operators.
166
+ *
167
+ * @private function of `createOrFilterCondition`
168
+ */
169
+ function normalizePostgrestOperator(operator: string): LocalSqliteFilter['operator'] {
170
+ switch (operator) {
171
+ case 'neq':
172
+ return 'neq';
173
+ case 'ilike':
174
+ return 'ilike';
175
+ case 'like':
176
+ return 'like';
177
+ case 'lt':
178
+ return 'lt';
179
+ case 'lte':
180
+ return 'lte';
181
+ case 'gt':
182
+ return 'gt';
183
+ case 'gte':
184
+ return 'gte';
185
+ case 'eq':
186
+ default:
187
+ return 'eq';
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Decodes a PostgREST filter value when URL-encoded by callers.
193
+ *
194
+ * @private function of `createOrFilterCondition`
195
+ */
196
+ function decodePostgrestFilterValue(value: string): string {
197
+ try {
198
+ return decodeURIComponent(value);
199
+ } catch {
200
+ return value;
201
+ }
202
+ }
@@ -0,0 +1,131 @@
1
+ import type { TODO_any } from '@promptbook-local/types';
2
+
3
+ /**
4
+ * Converts mutation payloads into a uniform array of records.
5
+ *
6
+ * @private function of `$provideLocalSqliteSupabase`
7
+ */
8
+ export function normalizeMutationRows(values: TODO_any): Array<Record<string, unknown>> {
9
+ if (Array.isArray(values)) {
10
+ return values.map((row) => stripUndefinedValues(row || {}));
11
+ }
12
+
13
+ return [stripUndefinedValues(values || {})];
14
+ }
15
+
16
+ /**
17
+ * Removes undefined values because Supabase omits them from mutation payloads.
18
+ *
19
+ * @private function of `$provideLocalSqliteSupabase`
20
+ */
21
+ export function stripUndefinedValues(values: Record<string, unknown>): Record<string, unknown> {
22
+ const result: Record<string, unknown> = {};
23
+
24
+ for (const [key, value] of Object.entries(values)) {
25
+ if (value !== undefined) {
26
+ result[key] = value;
27
+ }
28
+ }
29
+
30
+ return result;
31
+ }
32
+
33
+ /**
34
+ * Adds SQLite-side defaults that are normally supplied by PostgreSQL migrations.
35
+ *
36
+ * @private function of `$provideLocalSqliteSupabase`
37
+ */
38
+ export function withInsertDefaults(tableBaseName: string, row: Record<string, unknown>): Record<string, unknown> {
39
+ const nowIso = new Date().toISOString();
40
+ const result = { ...row };
41
+
42
+ if (result.createdAt === undefined) {
43
+ result.createdAt = nowIso;
44
+ }
45
+ if (result.updatedAt === undefined && tableBaseName !== 'AgentHistory' && tableBaseName !== 'ChatHistory') {
46
+ result.updatedAt = nowIso;
47
+ }
48
+
49
+ switch (tableBaseName) {
50
+ case 'Agent':
51
+ result.visibility ??= 'PRIVATE';
52
+ result.folderId ??= null;
53
+ result.sortOrder ??= Date.now();
54
+ result.deletedAt ??= null;
55
+ result.preparedModelRequirements ??= null;
56
+ break;
57
+ case 'AgentFolder':
58
+ result.parentId ??= null;
59
+ result.sortOrder ??= Date.now();
60
+ result.deletedAt ??= null;
61
+ result.icon ??= null;
62
+ result.color ??= null;
63
+ break;
64
+ case 'User':
65
+ result.isAdmin ??= false;
66
+ result.profileImageUrl ??= null;
67
+ result.email ??= null;
68
+ result.displayName ??= null;
69
+ result.authenticationProvider ??= 'LOCAL';
70
+ break;
71
+ case 'ShibbolethUserIdentity':
72
+ result.displayName ??= null;
73
+ result.nameId ??= null;
74
+ result.nameIdFormat ??= null;
75
+ result.unstructuredName ??= null;
76
+ result.eduPersonPrincipalName ??= null;
77
+ result.rawAttributes ??= null;
78
+ result.lastLoggedInAt ??= null;
79
+ result.loginCount ??= 0;
80
+ break;
81
+ case 'ShibbolethAuthenticationAttempt':
82
+ result.userId ??= null;
83
+ result.email ??= null;
84
+ result.displayName ??= null;
85
+ result.nameId ??= null;
86
+ result.relayState ??= null;
87
+ result.ip ??= null;
88
+ result.userAgent ??= null;
89
+ result.errorMessage ??= null;
90
+ result.rawAttributes ??= null;
91
+ break;
92
+ case 'UserChat':
93
+ result.messages ??= [];
94
+ result.source ??= 'WEB_UI';
95
+ result.title ??= null;
96
+ result.draftMessage ??= null;
97
+ break;
98
+ case 'UserChatJob':
99
+ result.parameters ??= {};
100
+ result.queuedAt ??= nowIso;
101
+ result.attemptCount ??= 0;
102
+ break;
103
+ case 'UserChatTimeout':
104
+ result.parameters ??= {};
105
+ result.queuedAt ??= nowIso;
106
+ result.attemptCount ??= 0;
107
+ result.runCount ??= 0;
108
+ break;
109
+ case 'ApiTokens':
110
+ result.isRevoked ??= false;
111
+ break;
112
+ case 'Wallet':
113
+ result.isUserScoped ??= true;
114
+ result.isGlobal ??= false;
115
+ result.deletedAt ??= null;
116
+ break;
117
+ case 'UserMemory':
118
+ result.isGlobal ??= false;
119
+ result.deletedAt ??= null;
120
+ break;
121
+ case 'ShareTargetPayload':
122
+ result.attachments ??= [];
123
+ result.consumedAt ??= null;
124
+ break;
125
+ case 'UserPushSubscription':
126
+ result.isChatFocused ??= false;
127
+ break;
128
+ }
129
+
130
+ return result;
131
+ }
@@ -0,0 +1,102 @@
1
+ import type { AgentsServerSqliteDatabase } from '../$provideAgentsServerSqliteDatabase';
2
+ import { createSelectExpression, quoteIdentifier } from './localSqliteSql';
3
+ import { deserializeRow, serializeValue } from './localSqliteValueCodec';
4
+ import { ensureTable } from './ensureTable';
5
+
6
+ /**
7
+ * Inserts one row into the table.
8
+ *
9
+ * @private function of `$provideLocalSqliteSupabase`
10
+ */
11
+ export function insertRow(
12
+ database: AgentsServerSqliteDatabase,
13
+ tableName: string,
14
+ row: Record<string, unknown>,
15
+ ): { readonly lastInsertRowid: number | bigint } {
16
+ const columns = Object.keys(row).filter((column) => row[column] !== undefined);
17
+
18
+ if (columns.length === 0) {
19
+ return database.prepare(`INSERT INTO ${quoteIdentifier(tableName)} DEFAULT VALUES`).run();
20
+ }
21
+
22
+ const placeholders = columns.map(() => '?').join(', ');
23
+ const values = columns.map((column) => serializeValue(tableName, column, row[column]));
24
+ const sql = `INSERT INTO ${quoteIdentifier(tableName)} (${columns
25
+ .map(quoteIdentifier)
26
+ .join(', ')}) VALUES (${placeholders})`;
27
+
28
+ return database.prepare(sql).run(...values);
29
+ }
30
+
31
+ /**
32
+ * Updates one row by SQLite rowid.
33
+ *
34
+ * @private function of `$provideLocalSqliteSupabase`
35
+ */
36
+ export function updateRowid(
37
+ database: AgentsServerSqliteDatabase,
38
+ tableName: string,
39
+ rowid: number | bigint,
40
+ row: Record<string, unknown>,
41
+ ): void {
42
+ const columns = Object.keys(row).filter((column) => column !== 'id' && row[column] !== undefined);
43
+ if (columns.length === 0) {
44
+ return;
45
+ }
46
+
47
+ const assignments = columns.map((column) => `${quoteIdentifier(column)} = ?`).join(', ');
48
+ const values = columns.map((column) => serializeValue(tableName, column, row[column]));
49
+ database.prepare(`UPDATE ${quoteIdentifier(tableName)} SET ${assignments} WHERE rowid = ?`).run(...values, rowid);
50
+ }
51
+
52
+ /**
53
+ * Finds the rowid matching an upsert conflict target.
54
+ *
55
+ * @private function of `$provideLocalSqliteSupabase`
56
+ */
57
+ export function findConflictRowid(
58
+ database: AgentsServerSqliteDatabase,
59
+ tableName: string,
60
+ row: Record<string, unknown>,
61
+ conflictColumns: ReadonlyArray<string>,
62
+ ): number | bigint | null {
63
+ if (conflictColumns.some((column) => row[column] === undefined)) {
64
+ return null;
65
+ }
66
+
67
+ const conditions = conflictColumns.map((column) => `${quoteIdentifier(column)} = ?`).join(' AND ');
68
+ const values = conflictColumns.map((column) => serializeValue(tableName, column, row[column]));
69
+ const result = database
70
+ .prepare(`SELECT rowid FROM ${quoteIdentifier(tableName)} WHERE ${conditions} LIMIT 1`)
71
+ .get(...values);
72
+
73
+ return result ? (result.rowid as number | bigint) : null;
74
+ }
75
+
76
+ /**
77
+ * Selects rows by rowids for a mutation returning clause.
78
+ *
79
+ * @private function of `$provideLocalSqliteSupabase`
80
+ */
81
+ export function selectRowsByRowids(
82
+ database: AgentsServerSqliteDatabase,
83
+ tableName: string,
84
+ rowids: ReadonlyArray<number | bigint>,
85
+ selectedColumns: ReadonlyArray<string>,
86
+ ): Array<Record<string, unknown>> {
87
+ if (rowids.length === 0) {
88
+ return [];
89
+ }
90
+
91
+ ensureTable(database, tableName, selectedColumns);
92
+ const placeholders = rowids.map(() => '?').join(', ');
93
+ const rows = database
94
+ .prepare(
95
+ `SELECT ${createSelectExpression(selectedColumns)} FROM ${quoteIdentifier(
96
+ tableName,
97
+ )} WHERE rowid IN (${placeholders})`,
98
+ )
99
+ .all(...rowids);
100
+
101
+ return rows.map((row) => deserializeRow(tableName, row));
102
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Quotes one SQLite identifier.
3
+ *
4
+ * @private function of `$provideLocalSqliteSupabase`
5
+ */
6
+ export function quoteIdentifier(identifier: string): string {
7
+ return `"${identifier.replace(/"/g, '""')}"`;
8
+ }
9
+
10
+ /**
11
+ * Creates a safe identifier suffix for generated index names.
12
+ *
13
+ * @private function of `$provideLocalSqliteSupabase`
14
+ */
15
+ export function sanitizeSqlIdentifier(identifier: string): string {
16
+ return identifier.replace(/[^A-Za-z0-9_]/g, '_');
17
+ }
18
+
19
+ /**
20
+ * Creates a select expression from parsed columns.
21
+ *
22
+ * @private function of `$provideLocalSqliteSupabase`
23
+ */
24
+ export function createSelectExpression(columns: ReadonlyArray<string>): string {
25
+ if (columns.length === 0 || columns.includes('*')) {
26
+ return '*';
27
+ }
28
+
29
+ return columns.map(quoteIdentifier).join(', ');
30
+ }
31
+
32
+ /**
33
+ * Parses a simple Supabase select column list.
34
+ *
35
+ * @private function of `$provideLocalSqliteSupabase`
36
+ */
37
+ export function parseSelectedColumns(columns: string): Array<string> {
38
+ const trimmedColumns = columns.trim();
39
+ if (!trimmedColumns || trimmedColumns === '*') {
40
+ return ['*'];
41
+ }
42
+
43
+ return trimmedColumns
44
+ .split(',')
45
+ .map((column) => column.trim())
46
+ .filter(Boolean)
47
+ .map((column) => column.split(':').pop() || column)
48
+ .map((column) => column.replace(/\s+/g, ''))
49
+ .filter((column) => /^[A-Za-z_][A-Za-z0-9_]*$/u.test(column));
50
+ }
51
+
52
+ /**
53
+ * Deduplicates strings while preserving order.
54
+ *
55
+ * @private function of `$provideLocalSqliteSupabase`
56
+ */
57
+ export function uniqueStrings(values: ReadonlyArray<string>): Array<string> {
58
+ return Array.from(new Set(values.filter(Boolean)));
59
+ }