@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
@@ -120,6 +120,7 @@ header.logOut: Odhlásit se
120
120
  header.changePassword: Změnit heslo
121
121
  header.menuLabel: Nabídka
122
122
  header.myAccount: Můj účet
123
+ header.superAdmin: Super administrace
123
124
  header.administration: Administrace
124
125
  header.monitoringAndUsage: Monitoring a využití
125
126
  header.integrationsAndKeys: Integrace a klíče
@@ -134,6 +135,10 @@ header.mockedChats: Ukázkové chaty
134
135
  header.viewAllUsers: Zobrazit všechny uživatele
135
136
  header.createNewUser: Vytvořit nového uživatele
136
137
  header.servers: Servery
138
+ header.environmentVariables: Proměnné prostředí
139
+ header.database: Databáze
140
+ header.logs: Logy
141
+ header.codeRunners: Code runnery
137
142
  header.models: Modely
138
143
  header.openApiDocumentation: Dokumentace OpenAPI
139
144
  header.apiTokens: API tokeny
@@ -122,6 +122,7 @@ header.logOut: Log out
122
122
  header.changePassword: Change Password
123
123
  header.menuLabel: Menu
124
124
  header.myAccount: My Account
125
+ header.superAdmin: Super Admin
125
126
  header.administration: Administration
126
127
  header.monitoringAndUsage: Monitoring & Usage
127
128
  header.integrationsAndKeys: Integrations & Keys
@@ -136,6 +137,10 @@ header.mockedChats: Mocked Chats
136
137
  header.viewAllUsers: View all users
137
138
  header.createNewUser: Create new user
138
139
  header.servers: Servers
140
+ header.environmentVariables: Environment variables
141
+ header.database: Database
142
+ header.logs: Logs
143
+ header.codeRunners: Code runners
139
144
  header.models: Models
140
145
  header.openApiDocumentation: OpenAPI Documentation
141
146
  header.apiTokens: API Tokens
@@ -55,6 +55,14 @@ const getCachedProvidedServer = cache(async (): Promise<ProvidedServer> => {
55
55
  });
56
56
 
57
57
  if (!resolvedSqliteServer) {
58
+ if (isIpAddressHost(requestHost)) {
59
+ return {
60
+ id: null,
61
+ publicUrl: resolveFallbackPublicUrl(requestHost),
62
+ tablePrefix: SUPABASE_TABLE_PREFIX,
63
+ };
64
+ }
65
+
58
66
  throw new Error(`Server with host "${requestHost}" is not registered in SERVERS`);
59
67
  }
60
68
 
@@ -156,3 +164,22 @@ function isLocalDevelopmentHost(host: string | null): boolean {
156
164
  normalizedHost.startsWith('[::1]:')
157
165
  );
158
166
  }
167
+
168
+ /**
169
+ * Checks whether the current request host is a raw IP address.
170
+ *
171
+ * @param host - Raw request host.
172
+ * @returns `true` when the host is IPv4 or IPv6.
173
+ */
174
+ function isIpAddressHost(host: string | null): boolean {
175
+ if (!host) {
176
+ return false;
177
+ }
178
+
179
+ const hostname = host
180
+ .trim()
181
+ .replace(/^\[(.+)\](?::\d+)?$/u, '$1')
182
+ .replace(/:\d+$/u, '');
183
+
184
+ return /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(hostname) || hostname.includes(':');
185
+ }
@@ -241,7 +241,11 @@ export function createServerPublicUrl(domain: string): URL {
241
241
  }
242
242
 
243
243
  const protocol =
244
- normalizedDomain.startsWith('localhost') || normalizedDomain.startsWith('127.0.0.1') ? 'http' : 'https';
244
+ normalizedDomain.startsWith('localhost') ||
245
+ normalizedDomain.startsWith('127.0.0.1') ||
246
+ isIpAddressHost(normalizedDomain)
247
+ ? 'http'
248
+ : 'https';
245
249
  return new URL(`${protocol}://${normalizedDomain}`);
246
250
  }
247
251
 
@@ -435,6 +439,21 @@ function hasHttpProtocol(value: string): boolean {
435
439
  return value.startsWith('http://') || value.startsWith('https://');
436
440
  }
437
441
 
442
+ /**
443
+ * Checks whether a normalized host is a raw IPv4 or IPv6 address.
444
+ *
445
+ * @param host - Normalized host or host with port.
446
+ * @returns `true` for raw IP hosts.
447
+ */
448
+ function isIpAddressHost(host: string): boolean {
449
+ const hostname = host
450
+ .trim()
451
+ .replace(/^\[(.+)\](?::\d+)?$/u, '$1')
452
+ .replace(/:\d+$/u, '');
453
+
454
+ return /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(hostname) || hostname.includes(':');
455
+ }
456
+
438
457
  /**
439
458
  * Checks whether a port is implicit for the given protocol and can be omitted.
440
459
  *
@@ -1,5 +1,5 @@
1
1
  import { createHmac } from 'crypto';
2
- import { cookies } from 'next/headers';
2
+ import { cookies, headers } from 'next/headers';
3
3
  import { cache } from 'react';
4
4
 
5
5
  /**
@@ -30,6 +30,36 @@ export type SessionUser = {
30
30
  readonly isGlobalAdmin?: boolean;
31
31
  };
32
32
 
33
+ /**
34
+ * Request details used to decide whether the auth cookie may require HTTPS.
35
+ */
36
+ export type SessionCookieSecurityContext = {
37
+ /**
38
+ * Current deployment mode.
39
+ */
40
+ readonly isProduction: boolean;
41
+ /**
42
+ * Raw `Host` header.
43
+ */
44
+ readonly host: string | null;
45
+ /**
46
+ * Raw forwarded host header emitted by the reverse proxy.
47
+ */
48
+ readonly forwardedHost: string | null;
49
+ /**
50
+ * Raw forwarded protocol header emitted by the reverse proxy.
51
+ */
52
+ readonly forwardedProto: string | null;
53
+ /**
54
+ * Comma-separated configured domain list from `SERVERS`.
55
+ */
56
+ readonly configuredServers: string | null | undefined;
57
+ /**
58
+ * Known standalone VPS public IP address.
59
+ */
60
+ readonly publicIpAddress: string | null | undefined;
61
+ };
62
+
33
63
  /**
34
64
  * Signs a session payload and serializes it into the cookie token format.
35
65
  *
@@ -77,6 +107,42 @@ export function parseSessionToken(token: string | null | undefined): SessionUser
77
107
  }
78
108
  }
79
109
 
110
+ /**
111
+ * Decides whether the session cookie should keep the `Secure` flag for the current request.
112
+ *
113
+ * This keeps production-domain logins protected by HTTPS while allowing the standalone
114
+ * VPS bootstrap flow to authenticate over `http://<IP_ADDRESS>` before any domain exists.
115
+ *
116
+ * @param context - Request and deployment details used for the decision.
117
+ * @returns `true` when the cookie should require HTTPS.
118
+ */
119
+ export function shouldUseSecureSessionCookieForRequest(context: SessionCookieSecurityContext): boolean {
120
+ if (!context.isProduction) {
121
+ return false;
122
+ }
123
+
124
+ if (parseConfiguredServers(context.configuredServers).length > 0) {
125
+ return true;
126
+ }
127
+
128
+ const requestHost = normalizeHost(context.forwardedHost ?? context.host);
129
+ if (!requestHost || !isIpAddressHost(requestHost)) {
130
+ return true;
131
+ }
132
+
133
+ const forwardedProtocol = (context.forwardedProto || '').split(',')[0]?.trim().toLowerCase() || '';
134
+ if (forwardedProtocol === 'https') {
135
+ return true;
136
+ }
137
+
138
+ const configuredPublicIpAddress = normalizeHost(context.publicIpAddress || '');
139
+ if (configuredPublicIpAddress && requestHost !== configuredPublicIpAddress) {
140
+ return true;
141
+ }
142
+
143
+ return false;
144
+ }
145
+
80
146
  /**
81
147
  * Persists the provided session payload into the signed session cookie.
82
148
  *
@@ -84,10 +150,11 @@ export function parseSessionToken(token: string | null | undefined): SessionUser
84
150
  */
85
151
  export async function setSession(user: SessionUser) {
86
152
  const token = serializeSessionToken(user);
153
+ const secure = await shouldUseSecureSessionCookie();
87
154
 
88
155
  (await cookies()).set(SESSION_COOKIE_NAME, token, {
89
156
  httpOnly: true,
90
- secure: process.env.NODE_ENV === 'production',
157
+ secure,
91
158
  path: '/',
92
159
  maxAge: 60 * 60 * 24 * 365 * 2, // 2 years
93
160
  });
@@ -120,3 +187,57 @@ const getCachedSession = cache(async (): Promise<SessionUser | null> => {
120
187
  export async function getSession(): Promise<SessionUser | null> {
121
188
  return getCachedSession();
122
189
  }
190
+
191
+ /**
192
+ * Resolves the runtime cookie security decision from the current request headers.
193
+ *
194
+ * @returns `true` when the session cookie should keep the `Secure` flag.
195
+ */
196
+ async function shouldUseSecureSessionCookie(): Promise<boolean> {
197
+ const headerStore = await headers();
198
+
199
+ return shouldUseSecureSessionCookieForRequest({
200
+ isProduction: process.env.NODE_ENV === 'production',
201
+ host: headerStore.get('host'),
202
+ forwardedHost: headerStore.get('x-forwarded-host'),
203
+ forwardedProto: headerStore.get('x-forwarded-proto'),
204
+ configuredServers: process.env.SERVERS,
205
+ publicIpAddress: process.env.PTBK_PUBLIC_IP_ADDRESS,
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Parses the configured `SERVERS` CSV into non-empty entries.
211
+ *
212
+ * @param configuredServers - Raw environment value.
213
+ * @returns Normalized list of configured domains.
214
+ */
215
+ function parseConfiguredServers(configuredServers: string | null | undefined): Array<string> {
216
+ return (configuredServers || '')
217
+ .split(',')
218
+ .map((server) => server.trim())
219
+ .filter(Boolean);
220
+ }
221
+
222
+ /**
223
+ * Checks whether a host string points to a raw IPv4 or IPv6 address.
224
+ *
225
+ * @param host - Host header or hostname.
226
+ * @returns `true` when the host is a raw IP address.
227
+ */
228
+ function isIpAddressHost(host: string): boolean {
229
+ return /^\d{1,3}(?:\.\d{1,3}){3}$/u.test(host) || host.includes(':');
230
+ }
231
+
232
+ /**
233
+ * Removes ports and IPv6 brackets from host-like strings.
234
+ *
235
+ * @param host - Raw host header value.
236
+ * @returns Normalized bare hostname or IP address.
237
+ */
238
+ function normalizeHost(host: string | null | undefined): string {
239
+ return (host || '')
240
+ .trim()
241
+ .replace(/^\[(.+)\](?::\d+)?$/u, '$1')
242
+ .replace(/:\d+$/u, '');
243
+ }