@promptbook/cli 0.112.0-93 → 0.112.0-96

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 (52) hide show
  1. package/apps/agents-server/next.config.ts +8 -1
  2. package/apps/agents-server/playwright.config.ts +2 -0
  3. package/apps/agents-server/src/app/admin/_components/AdminConfigurationShell.tsx +13 -7
  4. package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +225 -0
  5. package/apps/agents-server/src/app/admin/code-runners/page.tsx +14 -0
  6. package/apps/agents-server/src/app/admin/database/DatabaseAdminClient.tsx +38 -0
  7. package/apps/agents-server/src/app/admin/database/DatabaseAdminStudioSurface.tsx +42 -0
  8. package/apps/agents-server/src/app/admin/database/page.tsx +33 -0
  9. package/apps/agents-server/src/app/admin/environment/EnvironmentVariablesClient.tsx +259 -0
  10. package/apps/agents-server/src/app/admin/environment/page.tsx +21 -0
  11. package/apps/agents-server/src/app/admin/logs/LogsClient.tsx +78 -0
  12. package/apps/agents-server/src/app/admin/logs/page.tsx +14 -0
  13. package/apps/agents-server/src/app/admin/servers/ServersClient.tsx +64 -33
  14. package/apps/agents-server/src/app/admin/servers/ServersRegistryApi.ts +5 -0
  15. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +15 -2
  16. package/apps/agents-server/src/app/admin/servers/page.tsx +3 -3
  17. package/apps/agents-server/src/app/admin/servers/useServersRegistryState.ts +12 -2
  18. package/apps/agents-server/src/app/api/admin/code-runners/route.ts +104 -0
  19. package/apps/agents-server/src/app/api/admin/database/studio/route.ts +113 -0
  20. package/apps/agents-server/src/app/api/admin/environment/route.ts +65 -0
  21. package/apps/agents-server/src/app/api/admin/logs/route.ts +24 -0
  22. package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +79 -3
  23. package/apps/agents-server/src/app/api/admin/servers/route.ts +36 -1
  24. package/apps/agents-server/src/app/layout.tsx +1 -0
  25. package/apps/agents-server/src/app/page.tsx +101 -1
  26. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +27 -4
  27. package/apps/agents-server/src/database/$provideClientSql.ts +2 -6
  28. package/apps/agents-server/src/database/$provideDatabaseAdminExecutor.ts +273 -0
  29. package/apps/agents-server/src/database/resolvePostgresConnectionString.ts +26 -0
  30. package/apps/agents-server/src/database/sqlite/$provideAgentsServerSqliteDatabase.ts +83 -0
  31. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +20 -71
  32. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +5 -0
  33. package/apps/agents-server/src/languages/translations/czech.yaml +5 -0
  34. package/apps/agents-server/src/languages/translations/english.yaml +5 -0
  35. package/apps/agents-server/src/tools/$provideServer.ts +27 -0
  36. package/apps/agents-server/src/utils/serverRegistry.ts +20 -1
  37. package/apps/agents-server/src/utils/session.ts +123 -2
  38. package/apps/agents-server/src/utils/vpsConfiguration.ts +550 -0
  39. package/esm/index.es.js +1 -1
  40. package/esm/index.es.js.map +1 -1
  41. package/esm/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
  42. package/esm/src/version.d.ts +1 -1
  43. package/package.json +3 -1
  44. package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +9 -398
  45. package/src/book-components/Chat/utils/renderMarkdown.ts +323 -8
  46. package/src/other/templates/getTemplatesPipelineCollection.ts +712 -829
  47. package/src/version.ts +2 -2
  48. package/src/versions.txt +2 -0
  49. package/umd/index.umd.js +1 -1
  50. package/umd/index.umd.js.map +1 -1
  51. package/umd/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
  52. package/umd/src/version.d.ts +1 -1
@@ -28,6 +28,7 @@ type HeaderTranslate = (key: ServerTranslationKey, variables?: Readonly<Record<s
28
28
  type SystemCategoryLabel =
29
29
  | 'My Account'
30
30
  | 'Utilities'
31
+ | 'Super Admin'
31
32
  | 'Administration'
32
33
  | 'Monitoring & Usage'
33
34
  | 'Integrations & Keys'
@@ -54,6 +55,7 @@ type BuildHeaderSystemMenuItemsOptions = {
54
55
  const SYSTEM_CATEGORY_ICON_MAP: Record<SystemCategoryLabel, LucideIcon> = {
55
56
  'My Account': UserRound,
56
57
  Utilities: Wrench,
58
+ 'Super Admin': Settings2,
57
59
  Administration: Settings2,
58
60
  'Monitoring & Usage': BarChart3,
59
61
  'Integrations & Keys': KeyRound,
@@ -67,6 +69,7 @@ const SYSTEM_CATEGORY_ICON_MAP: Record<SystemCategoryLabel, LucideIcon> = {
67
69
  const SYSTEM_CATEGORY_TRANSLATION_KEY_MAP: Record<SystemCategoryLabel, ServerTranslationKey> = {
68
70
  'My Account': 'header.myAccount',
69
71
  Utilities: 'header.utilities',
72
+ 'Super Admin': 'header.superAdmin',
70
73
  Administration: 'header.administration',
71
74
  'Monitoring & Usage': 'header.monitoringAndUsage',
72
75
  'Integrations & Keys': 'header.integrationsAndKeys',
@@ -187,16 +190,35 @@ export function buildHeaderSystemMenuItems({
187
190
  ];
188
191
  }
189
192
 
190
- const administrationSystemItems: SubMenuItem[] = [
193
+ const superAdminSystemItems: SubMenuItem[] = [
194
+ {
195
+ label: translate('header.servers'),
196
+ href: '/admin/servers',
197
+ isBold: true,
198
+ },
199
+ {
200
+ label: translate('header.environmentVariables'),
201
+ href: '/admin/environment',
202
+ },
191
203
  ...(isGlobalAdmin
192
204
  ? [
193
205
  {
194
- label: translate('header.servers'),
195
- href: '/admin/servers',
196
- isBold: true,
206
+ label: translate('header.database'),
207
+ href: '/admin/database',
208
+ } as SubMenuItem,
209
+ {
210
+ label: translate('header.logs'),
211
+ href: '/admin/logs',
212
+ } as SubMenuItem,
213
+ {
214
+ label: translate('header.codeRunners'),
215
+ href: '/admin/code-runners',
197
216
  } as SubMenuItem,
198
217
  ]
199
218
  : []),
219
+ ];
220
+
221
+ const administrationSystemItems: SubMenuItem[] = [
200
222
  {
201
223
  label: translate('header.models'),
202
224
  href: '/admin/models',
@@ -311,6 +333,7 @@ export function buildHeaderSystemMenuItems({
311
333
  return [
312
334
  ...createSystemCategory('My Account', userAccountSystemItems, translate),
313
335
  ...createSystemCategory('Utilities', utilitiesSystemItems, translate),
336
+ ...createSystemCategory('Super Admin', superAdminSystemItems, translate),
314
337
  ...createSystemCategory('Administration', administrationSystemItems, translate),
315
338
  ...createSystemCategory('Monitoring & Usage', monitoringAndUsageSystemItems, translate),
316
339
  ...createSystemCategory('Integrations & Keys', integrationsAndKeysSystemItems, translate),
@@ -1,5 +1,6 @@
1
1
  import { $isRunningInNode } from '@promptbook-local/utils';
2
2
  import { Pool } from 'pg';
3
+ import { resolvePostgresConnectionString } from './resolvePostgresConnectionString';
3
4
 
4
5
  /**
5
6
  * SQL tagged-template executor used by server routes and utilities.
@@ -48,13 +49,8 @@ export async function $provideClientSql(): Promise<ClientSqlExecutor> {
48
49
  }
49
50
 
50
51
  if (!clientPool) {
51
- const connectionString = process.env.POSTGRES_URL || process.env.DATABASE_URL;
52
- if (!connectionString) {
53
- throw new Error('Environment variable `POSTGRES_URL` or `DATABASE_URL` must be defined.');
54
- }
55
-
56
52
  clientPool = new Pool({
57
- connectionString,
53
+ connectionString: resolvePostgresConnectionString(),
58
54
  ssl: { rejectUnauthorized: false },
59
55
  });
60
56
  }
@@ -0,0 +1,273 @@
1
+ import type { StudioBFFSqlLintDetails, StudioBFFSqlLintResult } from '@prisma/studio-core/data/bff';
2
+ import { Pool, type PoolClient } from 'pg';
3
+ import { isAgentsServerSqliteMode } from './agentsServerDatabaseMode';
4
+ import { resolvePostgresConnectionString } from './resolvePostgresConnectionString';
5
+ import {
6
+ $provideAgentsServerSqliteDatabase,
7
+ type AgentsServerSqliteDatabase,
8
+ } from './sqlite/$provideAgentsServerSqliteDatabase';
9
+
10
+ /**
11
+ * Query shape sent by Embedded Prisma Studio adapters.
12
+ *
13
+ * @private internal utility of Agents Server database admin
14
+ */
15
+ export type DatabaseAdminQuery = {
16
+ readonly sql: string;
17
+ readonly parameters?: ReadonlyArray<unknown>;
18
+ readonly transformations?: Partial<Record<string, 'json-parse'>>;
19
+ };
20
+
21
+ /**
22
+ * SQL executor surface required by the Embedded Prisma Studio BFF endpoint.
23
+ *
24
+ * @private internal utility of Agents Server database admin
25
+ */
26
+ export type DatabaseAdminExecutor = {
27
+ readonly execute: (query: DatabaseAdminQuery) => Promise<Array<Record<string, unknown>>>;
28
+ readonly executeTransaction: (
29
+ queries: ReadonlyArray<DatabaseAdminQuery>,
30
+ ) => Promise<Array<Array<Record<string, unknown>>>>;
31
+ readonly lintSql: (details: StudioBFFSqlLintDetails) => Promise<StudioBFFSqlLintResult>;
32
+ };
33
+
34
+ /**
35
+ * Shared PostgreSQL pool for Embedded Prisma Studio requests.
36
+ */
37
+ let databaseAdminPostgresPool: Pool | null = null;
38
+
39
+ /**
40
+ * Provides a raw SQL executor for the configured Agents Server database backend.
41
+ *
42
+ * @returns Database admin executor for the active database mode.
43
+ *
44
+ * @private exported from Agents Server database utilities
45
+ */
46
+ export function $provideDatabaseAdminExecutor(): DatabaseAdminExecutor {
47
+ return isAgentsServerSqliteMode() ? $provideSqliteDatabaseAdminExecutor() : $providePostgresDatabaseAdminExecutor();
48
+ }
49
+
50
+ /**
51
+ * Provides the PostgreSQL raw SQL executor used by Embedded Prisma Studio.
52
+ *
53
+ * @returns PostgreSQL-backed database admin executor.
54
+ */
55
+ function $providePostgresDatabaseAdminExecutor(): DatabaseAdminExecutor {
56
+ const pool = $provideDatabaseAdminPostgresPool();
57
+
58
+ return {
59
+ execute: (query) => executePostgresDatabaseAdminQuery(pool, query),
60
+ executeTransaction: (queries) => executePostgresDatabaseAdminTransaction(pool, queries),
61
+ lintSql: (details) => Promise.resolve({ diagnostics: [], schemaVersion: details.schemaVersion }),
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Provides the SQLite raw SQL executor used by Embedded Prisma Studio.
67
+ *
68
+ * @returns SQLite-backed database admin executor.
69
+ */
70
+ function $provideSqliteDatabaseAdminExecutor(): DatabaseAdminExecutor {
71
+ const database = $provideAgentsServerSqliteDatabase();
72
+
73
+ return {
74
+ execute: (query) => Promise.resolve(executeSqliteDatabaseAdminQuery(database, query)),
75
+ executeTransaction: (queries) =>
76
+ Promise.resolve(
77
+ database.transaction((transactionQueries: ReadonlyArray<DatabaseAdminQuery>) =>
78
+ transactionQueries.map((query) => executeSqliteDatabaseAdminQuery(database, query)),
79
+ )(queries),
80
+ ),
81
+ lintSql: (details) => Promise.resolve({ diagnostics: [], schemaVersion: details.schemaVersion }),
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Provides the shared PostgreSQL connection pool for raw database admin access.
87
+ *
88
+ * @returns Shared PostgreSQL pool.
89
+ */
90
+ function $provideDatabaseAdminPostgresPool(): Pool {
91
+ if (!databaseAdminPostgresPool) {
92
+ databaseAdminPostgresPool = new Pool({
93
+ connectionString: resolvePostgresConnectionString(),
94
+ ssl: { rejectUnauthorized: false },
95
+ });
96
+ }
97
+
98
+ return databaseAdminPostgresPool;
99
+ }
100
+
101
+ /**
102
+ * Executes one PostgreSQL query for Embedded Prisma Studio.
103
+ *
104
+ * @param client - PostgreSQL pool or transaction client.
105
+ * @param query - SQL query generated by Embedded Prisma Studio.
106
+ * @returns JSON-safe result rows.
107
+ */
108
+ async function executePostgresDatabaseAdminQuery(
109
+ client: Pick<Pool | PoolClient, 'query'>,
110
+ query: DatabaseAdminQuery,
111
+ ): Promise<Array<Record<string, unknown>>> {
112
+ const result = await client.query(query.sql, normalizeDatabaseAdminParameters(query.parameters));
113
+ return normalizeDatabaseAdminRows(query, result.rows as Array<Record<string, unknown>>);
114
+ }
115
+
116
+ /**
117
+ * Executes PostgreSQL queries inside one database transaction.
118
+ *
119
+ * @param pool - Shared PostgreSQL pool.
120
+ * @param queries - SQL queries generated by Embedded Prisma Studio.
121
+ * @returns Ordered result sets for each query.
122
+ */
123
+ async function executePostgresDatabaseAdminTransaction(
124
+ pool: Pool,
125
+ queries: ReadonlyArray<DatabaseAdminQuery>,
126
+ ): Promise<Array<Array<Record<string, unknown>>>> {
127
+ const client = await pool.connect();
128
+
129
+ try {
130
+ await client.query('BEGIN');
131
+ const results: Array<Array<Record<string, unknown>>> = [];
132
+
133
+ for (const query of queries) {
134
+ results.push(await executePostgresDatabaseAdminQuery(client, query));
135
+ }
136
+
137
+ await client.query('COMMIT');
138
+ return results;
139
+ } catch (error) {
140
+ await client.query('ROLLBACK').catch(() => undefined);
141
+ throw error;
142
+ } finally {
143
+ client.release();
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Executes one SQLite query for Embedded Prisma Studio.
149
+ *
150
+ * @param database - Shared SQLite database.
151
+ * @param query - SQL query generated by Embedded Prisma Studio.
152
+ * @returns JSON-safe result rows.
153
+ */
154
+ function executeSqliteDatabaseAdminQuery(
155
+ database: AgentsServerSqliteDatabase,
156
+ query: DatabaseAdminQuery,
157
+ ): Array<Record<string, unknown>> {
158
+ const statement = database.prepare(query.sql);
159
+ const parameters = normalizeDatabaseAdminParameters(query.parameters);
160
+ const isReader = statement.reader === true;
161
+ const rows = isReader ? statement.all(...parameters) : [];
162
+
163
+ if (!isReader) {
164
+ statement.run(...parameters);
165
+ }
166
+
167
+ return normalizeDatabaseAdminRows(query, rows);
168
+ }
169
+
170
+ /**
171
+ * Normalizes query parameters before passing them into SQL drivers.
172
+ *
173
+ * @param parameters - Query parameters generated by Embedded Prisma Studio.
174
+ * @returns Parameters safe to bind to PostgreSQL or SQLite.
175
+ */
176
+ function normalizeDatabaseAdminParameters(parameters: ReadonlyArray<unknown> = []): Array<unknown> {
177
+ return parameters.map((value) => (value === undefined ? null : value));
178
+ }
179
+
180
+ /**
181
+ * Applies Studio result transformations and converts rows into JSON-safe objects.
182
+ *
183
+ * @param query - SQL query whose transformations should be applied.
184
+ * @param rows - Raw database rows.
185
+ * @returns JSON-safe transformed rows.
186
+ */
187
+ function normalizeDatabaseAdminRows(
188
+ query: DatabaseAdminQuery,
189
+ rows: ReadonlyArray<Record<string, unknown>>,
190
+ ): Array<Record<string, unknown>> {
191
+ return rows.map((row) => normalizeDatabaseAdminRow(applyDatabaseAdminQueryTransformations(query, row)));
192
+ }
193
+
194
+ /**
195
+ * Applies one query's declared result transformations to a row.
196
+ *
197
+ * @param query - SQL query whose transformations should be applied.
198
+ * @param row - Raw database row.
199
+ * @returns Transformed row.
200
+ */
201
+ function applyDatabaseAdminQueryTransformations(
202
+ query: DatabaseAdminQuery,
203
+ row: Record<string, unknown>,
204
+ ): Record<string, unknown> {
205
+ const transformations = query.transformations;
206
+
207
+ if (!transformations || Object.keys(transformations).length === 0) {
208
+ return row;
209
+ }
210
+
211
+ const transformedRow = { ...row };
212
+
213
+ for (const [column, transformation] of Object.entries(transformations)) {
214
+ if (transformation !== 'json-parse' || typeof transformedRow[column] !== 'string') {
215
+ continue;
216
+ }
217
+
218
+ try {
219
+ transformedRow[column] = JSON.parse(transformedRow[column] as string);
220
+ } catch (error) {
221
+ console.error(`Failed to JSON.parse database admin column "${column}".`, error);
222
+ }
223
+ }
224
+
225
+ return transformedRow;
226
+ }
227
+
228
+ /**
229
+ * Converts one database row into values that can be returned via `NextResponse.json`.
230
+ *
231
+ * @param row - Database row.
232
+ * @returns JSON-safe row.
233
+ */
234
+ function normalizeDatabaseAdminRow(row: Record<string, unknown>): Record<string, unknown> {
235
+ return Object.fromEntries(
236
+ Object.entries(row).map(([key, value]) => [key, normalizeDatabaseAdminValue(value)]),
237
+ ) as Record<string, unknown>;
238
+ }
239
+
240
+ /**
241
+ * Converts one database value into a JSON-safe representation.
242
+ *
243
+ * @param value - Database value.
244
+ * @returns JSON-safe value.
245
+ */
246
+ function normalizeDatabaseAdminValue(value: unknown): unknown {
247
+ if (typeof value === 'bigint') {
248
+ return value.toString();
249
+ }
250
+
251
+ if (value instanceof Date) {
252
+ return value.toISOString();
253
+ }
254
+
255
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
256
+ return value.toString('base64');
257
+ }
258
+
259
+ if (Array.isArray(value)) {
260
+ return value.map(normalizeDatabaseAdminValue);
261
+ }
262
+
263
+ if (typeof value === 'object' && value !== null) {
264
+ return Object.fromEntries(
265
+ Object.entries(value as Record<string, unknown>).map(([key, childValue]) => [
266
+ key,
267
+ normalizeDatabaseAdminValue(childValue),
268
+ ]),
269
+ );
270
+ }
271
+
272
+ return value;
273
+ }
@@ -0,0 +1,26 @@
1
+ import spaceTrim from 'spacetrim';
2
+ import { DatabaseError } from '../../../../src/errors/DatabaseError';
3
+
4
+ /**
5
+ * Resolves the PostgreSQL connection string used by Supabase-backed Agents Server database access.
6
+ *
7
+ * @returns PostgreSQL connection string.
8
+ * @throws DatabaseError when neither supported environment variable is configured.
9
+ *
10
+ * @private exported from Agents Server database utilities
11
+ */
12
+ export function resolvePostgresConnectionString(): string {
13
+ const connectionString = process.env.POSTGRES_URL || process.env.DATABASE_URL;
14
+
15
+ if (!connectionString) {
16
+ throw new DatabaseError(
17
+ spaceTrim(`
18
+ Environment variable \`POSTGRES_URL\` or \`DATABASE_URL\` must be defined.
19
+
20
+ Configure one of these variables to use the Supabase/PostgreSQL database backend.
21
+ `),
22
+ );
23
+ }
24
+
25
+ return connectionString;
26
+ }
@@ -0,0 +1,83 @@
1
+ import { existsSync, mkdirSync } from 'fs';
2
+ import { dirname } from 'path';
3
+ import { resolveAgentsServerSqliteDatabasePath } from './resolveAgentsServerSqliteDatabasePath';
4
+
5
+ /**
6
+ * Shape of the `better-sqlite3` module constructor loaded at runtime.
7
+ *
8
+ * @private internal SQLite utility of Agents Server
9
+ */
10
+ export type BetterSqliteConstructor = new (path: string) => AgentsServerSqliteDatabase;
11
+
12
+ /**
13
+ * Minimal `better-sqlite3` database surface shared by Agents Server SQLite utilities.
14
+ *
15
+ * @private internal SQLite utility of Agents Server
16
+ */
17
+ export type AgentsServerSqliteDatabase = {
18
+ readonly pragma: (source: string) => unknown;
19
+ readonly exec: (source: string) => void;
20
+ readonly prepare: (source: string) => AgentsServerSqliteStatement;
21
+ readonly transaction: <TArguments extends ReadonlyArray<unknown>, TResult>(
22
+ fn: (...args: TArguments) => TResult,
23
+ ) => (...args: TArguments) => TResult;
24
+ readonly close?: () => void;
25
+ };
26
+
27
+ /**
28
+ * Minimal `better-sqlite3` prepared statement surface shared by Agents Server SQLite utilities.
29
+ *
30
+ * @private internal SQLite utility of Agents Server
31
+ */
32
+ export type AgentsServerSqliteStatement = {
33
+ readonly reader?: boolean;
34
+ readonly all: (...values: ReadonlyArray<unknown>) => Array<Record<string, unknown>>;
35
+ readonly get: (...values: ReadonlyArray<unknown>) => Record<string, unknown> | undefined;
36
+ readonly run: (...values: ReadonlyArray<unknown>) => {
37
+ readonly changes: number;
38
+ readonly lastInsertRowid: number | bigint;
39
+ };
40
+ };
41
+
42
+ /**
43
+ * Cached SQLite database connection.
44
+ */
45
+ let agentsServerSqliteDatabase: AgentsServerSqliteDatabase | null = null;
46
+
47
+ /**
48
+ * Opens and initializes the shared local SQLite database.
49
+ *
50
+ * @returns Shared SQLite database connection.
51
+ *
52
+ * @private exported from Agents Server SQLite utilities
53
+ */
54
+ export function $provideAgentsServerSqliteDatabase(): AgentsServerSqliteDatabase {
55
+ if (agentsServerSqliteDatabase) {
56
+ return agentsServerSqliteDatabase;
57
+ }
58
+
59
+ const databasePath = resolveAgentsServerSqliteDatabasePath();
60
+ const databaseDirectory = dirname(databasePath);
61
+
62
+ if (!existsSync(databaseDirectory)) {
63
+ mkdirSync(databaseDirectory, { recursive: true });
64
+ }
65
+
66
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
67
+ const BetterSqlite = require('better-sqlite3') as BetterSqliteConstructor;
68
+ agentsServerSqliteDatabase = new BetterSqlite(databasePath);
69
+ agentsServerSqliteDatabase.pragma('journal_mode = WAL');
70
+ agentsServerSqliteDatabase.pragma('foreign_keys = ON');
71
+
72
+ return agentsServerSqliteDatabase;
73
+ }
74
+
75
+ /**
76
+ * Closes the cached SQLite connection and resets adapter state for isolated tests.
77
+ *
78
+ * @private exported from Agents Server SQLite utilities
79
+ */
80
+ export function $resetAgentsServerSqliteDatabaseForTests(): void {
81
+ agentsServerSqliteDatabase?.close?.();
82
+ agentsServerSqliteDatabase = null;
83
+ }
@@ -1,8 +1,10 @@
1
- import { existsSync, mkdirSync } from 'fs';
2
- import { dirname } from 'path';
3
1
  import type { SupabaseClient } from '@supabase/supabase-js';
4
2
  import type { TODO_any } from '@promptbook-local/types';
5
- import { resolveAgentsServerSqliteDatabasePath } from './resolveAgentsServerSqliteDatabasePath';
3
+ import {
4
+ $provideAgentsServerSqliteDatabase,
5
+ $resetAgentsServerSqliteDatabaseForTests,
6
+ type AgentsServerSqliteDatabase,
7
+ } from './$provideAgentsServerSqliteDatabase';
6
8
 
7
9
  /**
8
10
  * Minimal query result shape consumed by Agents Server Supabase call sites.
@@ -63,33 +65,6 @@ type LocalSqliteUpsertOptions = {
63
65
  readonly onConflict?: string;
64
66
  };
65
67
 
66
- /**
67
- * Shape of the `better-sqlite3` module constructor loaded at runtime.
68
- */
69
- type BetterSqliteConstructor = new (path: string) => BetterSqliteDatabase;
70
-
71
- /**
72
- * Minimal `better-sqlite3` database surface used by this adapter.
73
- */
74
- type BetterSqliteDatabase = {
75
- readonly pragma: (source: string) => unknown;
76
- readonly exec: (source: string) => void;
77
- readonly prepare: (source: string) => BetterSqliteStatement;
78
- readonly close?: () => void;
79
- };
80
-
81
- /**
82
- * Minimal `better-sqlite3` prepared statement surface used by this adapter.
83
- */
84
- type BetterSqliteStatement = {
85
- readonly all: (...values: ReadonlyArray<unknown>) => Array<Record<string, unknown>>;
86
- readonly get: (...values: ReadonlyArray<unknown>) => Record<string, unknown> | undefined;
87
- readonly run: (...values: ReadonlyArray<unknown>) => {
88
- readonly changes: number;
89
- readonly lastInsertRowid: number | bigint;
90
- };
91
- };
92
-
93
68
  /**
94
69
  * Columns whose values are persisted as JSON text in local SQLite.
95
70
  */
@@ -170,11 +145,6 @@ const DEFAULT_UPSERT_CONFLICT_COLUMNS_BY_TABLE = new Map<string, ReadonlyArray<s
170
145
  ['ServerLimit', ['key']],
171
146
  ]);
172
147
 
173
- /**
174
- * Cached SQLite database connection.
175
- */
176
- let sqliteDatabase: BetterSqliteDatabase | null = null;
177
-
178
148
  /**
179
149
  * Cached Supabase-shaped local client.
180
150
  */
@@ -188,7 +158,7 @@ export function $provideLocalSqliteSupabase(): SupabaseClient {
188
158
  return localSqliteSupabase;
189
159
  }
190
160
 
191
- localSqliteSupabase = new LocalSqliteSupabaseClient(getSqliteDatabase()) as unknown as SupabaseClient;
161
+ localSqliteSupabase = new LocalSqliteSupabaseClient($provideAgentsServerSqliteDatabase()) as unknown as SupabaseClient;
192
162
  return localSqliteSupabase;
193
163
  }
194
164
 
@@ -196,40 +166,15 @@ export function $provideLocalSqliteSupabase(): SupabaseClient {
196
166
  * Closes the cached SQLite connection and resets adapter state for isolated tests.
197
167
  */
198
168
  export function $resetLocalSqliteSupabaseForTests(): void {
199
- sqliteDatabase?.close?.();
200
- sqliteDatabase = null;
169
+ $resetAgentsServerSqliteDatabaseForTests();
201
170
  localSqliteSupabase = null;
202
171
  }
203
172
 
204
- /**
205
- * Opens and initializes the shared local SQLite database.
206
- */
207
- function getSqliteDatabase(): BetterSqliteDatabase {
208
- if (sqliteDatabase) {
209
- return sqliteDatabase;
210
- }
211
-
212
- const databasePath = resolveAgentsServerSqliteDatabasePath();
213
- const databaseDirectory = dirname(databasePath);
214
-
215
- if (!existsSync(databaseDirectory)) {
216
- mkdirSync(databaseDirectory, { recursive: true });
217
- }
218
-
219
- // eslint-disable-next-line @typescript-eslint/no-var-requires
220
- const BetterSqlite = require('better-sqlite3') as BetterSqliteConstructor;
221
- sqliteDatabase = new BetterSqlite(databasePath);
222
- sqliteDatabase.pragma('journal_mode = WAL');
223
- sqliteDatabase.pragma('foreign_keys = ON');
224
-
225
- return sqliteDatabase;
226
- }
227
-
228
173
  /**
229
174
  * Supabase-shaped client with only the table query surface used by Agents Server.
230
175
  */
231
176
  class LocalSqliteSupabaseClient {
232
- public constructor(private readonly database: BetterSqliteDatabase) {}
177
+ public constructor(private readonly database: AgentsServerSqliteDatabase) {}
233
178
 
234
179
  /**
235
180
  * Starts a query for one SQLite table.
@@ -244,7 +189,7 @@ class LocalSqliteSupabaseClient {
244
189
  */
245
190
  class LocalSqliteTable {
246
191
  public constructor(
247
- private readonly database: BetterSqliteDatabase,
192
+ private readonly database: AgentsServerSqliteDatabase,
248
193
  private readonly tableName: string,
249
194
  ) {}
250
195
 
@@ -304,7 +249,7 @@ class LocalSqliteQueryBuilder implements PromiseLike<LocalSqliteQueryResult> {
304
249
  private isReturningSelection = false;
305
250
 
306
251
  public constructor(
307
- private readonly database: BetterSqliteDatabase,
252
+ private readonly database: AgentsServerSqliteDatabase,
308
253
  private readonly tableName: string,
309
254
  ) {}
310
255
 
@@ -851,7 +796,11 @@ type ParsedPostgrestFilter = {
851
796
  /**
852
797
  * Ensures a table and all required columns exist.
853
798
  */
854
- function ensureTable(database: BetterSqliteDatabase, tableName: string, requiredColumns: ReadonlyArray<string>): void {
799
+ function ensureTable(
800
+ database: AgentsServerSqliteDatabase,
801
+ tableName: string,
802
+ requiredColumns: ReadonlyArray<string>,
803
+ ): void {
855
804
  const tableBaseName = resolveTableBaseName(tableName);
856
805
  const primaryKey = TEXT_PRIMARY_KEY_TABLES.has(tableBaseName)
857
806
  ? '"id" TEXT PRIMARY KEY'
@@ -886,7 +835,7 @@ function ensureTable(database: BetterSqliteDatabase, tableName: string, required
886
835
  /**
887
836
  * Creates known unique indexes after required columns exist.
888
837
  */
889
- function ensureUniqueIndexes(database: BetterSqliteDatabase, tableName: string, tableBaseName: string): void {
838
+ function ensureUniqueIndexes(database: AgentsServerSqliteDatabase, tableName: string, tableBaseName: string): void {
890
839
  const uniqueIndexes = UNIQUE_INDEX_COLUMNS_BY_TABLE.get(tableBaseName) || [];
891
840
 
892
841
  for (const columns of uniqueIndexes) {
@@ -900,7 +849,7 @@ function ensureUniqueIndexes(database: BetterSqliteDatabase, tableName: string,
900
849
  * Inserts one row into the table.
901
850
  */
902
851
  function insertRow(
903
- database: BetterSqliteDatabase,
852
+ database: AgentsServerSqliteDatabase,
904
853
  tableName: string,
905
854
  row: Record<string, unknown>,
906
855
  ): { readonly lastInsertRowid: number | bigint } {
@@ -921,7 +870,7 @@ function insertRow(
921
870
  * Updates one row by SQLite rowid.
922
871
  */
923
872
  function updateRowid(
924
- database: BetterSqliteDatabase,
873
+ database: AgentsServerSqliteDatabase,
925
874
  tableName: string,
926
875
  rowid: number | bigint,
927
876
  row: Record<string, unknown>,
@@ -940,7 +889,7 @@ function updateRowid(
940
889
  * Finds the rowid matching an upsert conflict target.
941
890
  */
942
891
  function findConflictRowid(
943
- database: BetterSqliteDatabase,
892
+ database: AgentsServerSqliteDatabase,
944
893
  tableName: string,
945
894
  row: Record<string, unknown>,
946
895
  conflictColumns: ReadonlyArray<string>,
@@ -960,7 +909,7 @@ function findConflictRowid(
960
909
  * Selects rows by rowids for a mutation returning clause.
961
910
  */
962
911
  function selectRowsByRowids(
963
- database: BetterSqliteDatabase,
912
+ database: AgentsServerSqliteDatabase,
964
913
  tableName: string,
965
914
  rowids: ReadonlyArray<number | bigint>,
966
915
  selectedColumns: ReadonlyArray<string>,
@@ -126,6 +126,7 @@ export const SERVER_TRANSLATION_KEYS = [
126
126
  'header.changePassword',
127
127
  'header.menuLabel',
128
128
  'header.myAccount',
129
+ 'header.superAdmin',
129
130
  'header.administration',
130
131
  'header.monitoringAndUsage',
131
132
  'header.integrationsAndKeys',
@@ -140,6 +141,10 @@ export const SERVER_TRANSLATION_KEYS = [
140
141
  'header.viewAllUsers',
141
142
  'header.createNewUser',
142
143
  'header.servers',
144
+ 'header.environmentVariables',
145
+ 'header.database',
146
+ 'header.logs',
147
+ 'header.codeRunners',
143
148
  'header.models',
144
149
  'header.openApiDocumentation',
145
150
  'header.apiTokens',