@promptbook/cli 0.112.0-100 → 0.112.0-102

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 (78) hide show
  1. package/apps/agents-server/README.md +6 -0
  2. package/apps/agents-server/package.json +1 -1
  3. package/apps/agents-server/scripts/prerender-homepage.js +76 -1
  4. package/apps/agents-server/src/app/actions.ts +0 -6
  5. package/apps/agents-server/src/app/admin/about/page.tsx +1 -1
  6. package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +365 -0
  7. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +3 -3
  8. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +12 -3
  9. package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +1 -1
  10. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +21 -14
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatPageLayout.tsx +2 -2
  12. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatSidebarDefault.tsx +11 -7
  13. package/apps/agents-server/src/app/api/admin/cli-access/route.ts +27 -123
  14. package/apps/agents-server/src/app/api/admin/code-runners/authentication/route.ts +33 -125
  15. package/apps/agents-server/src/app/api/auth/login/route.ts +0 -10
  16. package/apps/agents-server/src/app/api/auth/shibboleth/acs/route.ts +77 -57
  17. package/apps/agents-server/src/app/api/auth/shibboleth/login/route.ts +57 -33
  18. package/apps/agents-server/src/app/api/auth/shibboleth/metadata/route.ts +4 -29
  19. package/apps/agents-server/src/app/api/auth/shibboleth/status/route.ts +17 -0
  20. package/apps/agents-server/src/app/api/upload/route.ts +230 -18
  21. package/apps/agents-server/src/app/api/users/[username]/route.ts +1 -1
  22. package/apps/agents-server/src/app/api/users/route.ts +5 -5
  23. package/apps/agents-server/src/app/dashboard/page.tsx +1 -1
  24. package/apps/agents-server/src/app/docs/[docId]/page.tsx +1 -1
  25. package/apps/agents-server/src/app/docs/page.tsx +1 -1
  26. package/apps/agents-server/src/app/globals.css +100 -0
  27. package/apps/agents-server/src/app/layout.tsx +7 -0
  28. package/apps/agents-server/src/app/recycle-bin/page.tsx +1 -1
  29. package/apps/agents-server/src/app/system/settings/KeybindingsSettingsClient.tsx +13 -7
  30. package/apps/agents-server/src/components/AdminTerminal/useAdminTerminalSession.ts +29 -1
  31. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +3 -3
  32. package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +8 -2
  33. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +4 -4
  34. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +9 -9
  35. package/apps/agents-server/src/components/Footer/Footer.tsx +7 -7
  36. package/apps/agents-server/src/components/Header/Header.tsx +24 -4
  37. package/apps/agents-server/src/components/Header/HeaderTypes.ts +6 -0
  38. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +51 -1
  39. package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
  40. package/apps/agents-server/src/components/Homepage/Section.tsx +3 -1
  41. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +12 -1
  42. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +100 -149
  43. package/apps/agents-server/src/components/Skeleton/ConsolePageLoadingSkeleton.tsx +1 -1
  44. package/apps/agents-server/src/components/Skeleton/DocumentationRouteLoadingSkeleton.tsx +1 -1
  45. package/apps/agents-server/src/components/Skeleton/HomepageLoadingSkeleton.tsx +1 -1
  46. package/apps/agents-server/src/components/UsersList/UsersList.tsx +20 -4
  47. package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +3 -0
  48. package/apps/agents-server/src/constants/shibbolethAuth.ts +139 -0
  49. package/apps/agents-server/src/database/metadataDefaults.ts +54 -80
  50. package/apps/agents-server/src/database/migrate.ts +30 -1
  51. package/apps/agents-server/src/database/migrations/2026-06-0100-shibboleth-auth.sql +136 -0
  52. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +88 -36
  53. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +4 -2
  54. package/apps/agents-server/src/languages/translations/czech.yaml +4 -2
  55. package/apps/agents-server/src/languages/translations/english.yaml +5 -3
  56. package/apps/agents-server/src/tools/$provideCdnForServer.ts +69 -23
  57. package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +54 -6
  58. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +4 -6
  59. package/apps/agents-server/src/utils/cdn/resolveCdnStorageProvider.ts +40 -0
  60. package/apps/agents-server/src/utils/chatExport/renderHtmlToPdfOnServer.ts +11 -0
  61. package/apps/agents-server/src/utils/createAdminTerminalRouteHandlers.ts +264 -0
  62. package/apps/agents-server/src/utils/shareTargetPayloads.ts +11 -10
  63. package/apps/agents-server/src/utils/shibbolethAuthentication.ts +729 -621
  64. package/apps/agents-server/src/utils/upload/createBookEditorUploadHandler.ts +137 -19
  65. package/esm/index.es.js +1 -1
  66. package/esm/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  67. package/esm/src/version.d.ts +1 -1
  68. package/package.json +2 -2
  69. package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +65 -4
  70. package/src/other/templates/getTemplatesPipelineCollection.ts +877 -689
  71. package/src/version.ts +2 -2
  72. package/src/versions.txt +2 -0
  73. package/umd/index.umd.js +1 -1
  74. package/umd/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  75. package/umd/src/version.d.ts +1 -1
  76. package/apps/agents-server/src/app/api/auth/methods/route.ts +0 -44
  77. package/apps/agents-server/src/constants/authenticationMethods.ts +0 -74
  78. package/apps/agents-server/src/constants/shibbolethAuthentication.ts +0 -107
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { upload } from '@vercel/blob/client';
4
4
  import type { number_percent, string_knowledge_source_content } from '@promptbook-local/types';
5
+ import { isServerRoutedCdnUploadProvider, resolveCdnStorageProvider } from '../cdn/resolveCdnStorageProvider';
5
6
  import { getSafeCdnPath } from '../cdn/utils/getSafeCdnPath';
6
7
  import { normalizeUploadFilename } from '../normalization/normalizeUploadFilename';
7
8
 
@@ -75,6 +76,109 @@ type SharedUploadHandlerConfig = {
75
76
  */
76
77
  const DEFAULT_SHORT_URL_PREFIX = 'https://ptbk.io/k/';
77
78
 
79
+ /**
80
+ * Response returned by the server-routed upload API.
81
+ *
82
+ * @private used by chat and book editors.
83
+ */
84
+ type ServerRoutedUploadResponse = {
85
+ url: string;
86
+ };
87
+
88
+ /**
89
+ * Uploads a file through `/api/upload` for S3-compatible storage providers.
90
+ *
91
+ * @private used by chat and book editors.
92
+ */
93
+ function uploadFileThroughServer(options: {
94
+ safeUploadPath: string;
95
+ file: File;
96
+ purpose: string;
97
+ abortSignal?: AbortSignal;
98
+ onProgress?: FileUploadProgressCallback;
99
+ }): Promise<ServerRoutedUploadResponse> {
100
+ const { safeUploadPath, file, purpose, abortSignal, onProgress } = options;
101
+ const formData = new FormData();
102
+ formData.set('file', file);
103
+ formData.set('pathname', safeUploadPath);
104
+ formData.set('purpose', purpose);
105
+ formData.set('contentType', file.type || 'application/octet-stream');
106
+
107
+ return new Promise((resolve, reject) => {
108
+ const request = new XMLHttpRequest();
109
+
110
+ /**
111
+ * Removes abort listeners once the upload finishes.
112
+ */
113
+ const cleanup = () => {
114
+ abortSignal?.removeEventListener('abort', handleAbort);
115
+ };
116
+
117
+ /**
118
+ * Aborts the active XHR upload when the caller aborts.
119
+ */
120
+ const handleAbort = () => {
121
+ request.abort();
122
+ cleanup();
123
+ reject(new DOMException('Upload aborted.', 'AbortError'));
124
+ };
125
+
126
+ request.upload.addEventListener('progress', (event) => {
127
+ if (!event.lengthComputable) {
128
+ return;
129
+ }
130
+
131
+ onProgress?.((event.loaded / event.total) as number_percent, {
132
+ loadedBytes: event.loaded,
133
+ totalBytes: event.total,
134
+ });
135
+ });
136
+
137
+ request.addEventListener('load', () => {
138
+ cleanup();
139
+
140
+ if (request.status < 200 || request.status >= 300) {
141
+ reject(new Error(`Upload failed with HTTP ${request.status}: ${request.responseText}`));
142
+ return;
143
+ }
144
+
145
+ try {
146
+ const parsedResponse = JSON.parse(request.responseText) as Partial<ServerRoutedUploadResponse>;
147
+ if (typeof parsedResponse.url !== 'string' || parsedResponse.url.trim() === '') {
148
+ reject(new Error('Upload response did not contain a file URL.'));
149
+ return;
150
+ }
151
+
152
+ onProgress?.(1 as number_percent, {
153
+ loadedBytes: file.size,
154
+ totalBytes: file.size,
155
+ });
156
+ resolve({ url: parsedResponse.url });
157
+ } catch (error) {
158
+ reject(error);
159
+ }
160
+ });
161
+
162
+ request.addEventListener('error', () => {
163
+ cleanup();
164
+ reject(new Error('Upload failed before the server responded.'));
165
+ });
166
+
167
+ request.addEventListener('abort', () => {
168
+ cleanup();
169
+ });
170
+
171
+ if (abortSignal?.aborted) {
172
+ handleAbort();
173
+ return;
174
+ }
175
+
176
+ abortSignal?.addEventListener('abort', handleAbort);
177
+ request.open('POST', '/api/upload');
178
+ request.send(formData);
179
+ });
180
+ }
181
+
78
182
  /**
79
183
  * Upload handler that normalizes the filename, uploads via `/api/upload`, and optionally returns a short URL.
80
184
  *
@@ -92,30 +196,44 @@ export function createFileUploadHandler<ReturnType extends string = string>(
92
196
 
93
197
  return async (file, optionsOrOnProgress) => {
94
198
  const { onProgress, abortSignal } = normalizeUploadOptions(optionsOrOnProgress);
199
+ const provider = resolveCdnStorageProvider();
200
+ const isServerRoutedUploadEnabled = isServerRoutedCdnUploadProvider(provider);
95
201
  const pathPrefix = process.env.NEXT_PUBLIC_CDN_PATH_PREFIX || '';
96
202
  const normalizedFilename = normalizeUploadFilename(file.name);
97
- const uploadPath = pathBuilder(normalizedFilename, pathPrefix);
98
- const safeUploadPath = getSafeCdnPath({ pathname: uploadPath });
99
-
100
- const blob = await upload(safeUploadPath, file, {
101
- access: 'public',
102
- handleUploadUrl: '/api/upload',
103
- clientPayload: JSON.stringify({
104
- purpose,
105
- contentType: file.type || 'application/octet-stream',
106
- }),
107
- abortSignal,
108
- onUploadProgress: (progressEvent) => {
109
- onProgress?.(progressEvent.percentage / 100, {
110
- loadedBytes: progressEvent.loaded,
111
- totalBytes: progressEvent.total,
112
- });
113
- },
203
+ const publicUploadPath = pathBuilder(normalizedFilename, pathPrefix);
204
+ const storageUploadPath = pathBuilder(normalizedFilename, isServerRoutedUploadEnabled ? '' : pathPrefix);
205
+ const safeUploadPath = getSafeCdnPath({
206
+ pathname: storageUploadPath,
207
+ pathPrefix: isServerRoutedUploadEnabled ? pathPrefix : undefined,
114
208
  });
115
209
 
210
+ const blob = isServerRoutedUploadEnabled
211
+ ? await uploadFileThroughServer({
212
+ safeUploadPath,
213
+ file,
214
+ purpose,
215
+ abortSignal,
216
+ onProgress,
217
+ })
218
+ : await upload(safeUploadPath, file, {
219
+ access: 'public',
220
+ handleUploadUrl: '/api/upload',
221
+ clientPayload: JSON.stringify({
222
+ purpose,
223
+ contentType: file.type || 'application/octet-stream',
224
+ }),
225
+ abortSignal,
226
+ onUploadProgress: (progressEvent) => {
227
+ onProgress?.(progressEvent.percentage / 100, {
228
+ loadedBytes: progressEvent.loaded,
229
+ totalBytes: progressEvent.total,
230
+ });
231
+ },
232
+ });
233
+
116
234
  if (returnShortUrl && process.env.NEXT_PUBLIC_CDN_PUBLIC_URL) {
117
- const slashIndex = uploadPath.lastIndexOf('/');
118
- const directoryPath = slashIndex === -1 ? '' : `${uploadPath.slice(0, slashIndex + 1)}`;
235
+ const slashIndex = publicUploadPath.lastIndexOf('/');
236
+ const directoryPath = slashIndex === -1 ? '' : `${publicUploadPath.slice(0, slashIndex + 1)}`;
119
237
  const longUrlPrefix = `${process.env.NEXT_PUBLIC_CDN_PUBLIC_URL}/${directoryPath}`;
120
238
  const shortUrl = blob.url.split(longUrlPrefix).join(shortUrlPrefix);
121
239
  return shortUrl as ReturnType;
package/esm/index.es.js CHANGED
@@ -58,7 +58,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-100';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-102';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -7,6 +7,7 @@ type MarkdownContentProps = {
7
7
  content: string_markdown;
8
8
  className?: string;
9
9
  onCreateAgent?: (bookContent: string) => void;
10
+ theme?: 'LIGHT' | 'DARK';
10
11
  };
11
12
  /**
12
13
  * Renders markdown content with support for code highlighting, math, and tables.
@@ -15,7 +15,7 @@ export declare const BOOK_LANGUAGE_VERSION: string_semantic_version;
15
15
  export declare const PROMPTBOOK_ENGINE_VERSION: string_promptbook_version;
16
16
  /**
17
17
  * Represents the version string of the Promptbook engine.
18
- * It follows semantic versioning (e.g., `0.112.0-99`).
18
+ * It follows semantic versioning (e.g., `0.112.0-101`).
19
19
  *
20
20
  * @generated
21
21
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptbook/cli",
3
- "version": "0.112.0-100",
3
+ "version": "0.112.0-102",
4
4
  "description": "Promptbook: Create persistent AI agents that turn your company's scattered knowledge into action",
5
5
  "private": false,
6
6
  "sideEffects": false,
@@ -159,6 +159,7 @@
159
159
  "@types/swagger-ui-react": "5.18.0",
160
160
  "@types/turndown": "5.0.6",
161
161
  "@vercel/blob": "1.1.1",
162
+ "@xmldom/xmldom": "^0.9.10",
162
163
  "@xterm/addon-fit": "0.11.0",
163
164
  "@xterm/xterm": "6.0.0",
164
165
  "better-sqlite3": "^11.10.0",
@@ -166,7 +167,6 @@
166
167
  "cross-fetch": "4.0.0",
167
168
  "destroyable": "0.12.145",
168
169
  "dompurify": "3.4.6",
169
- "fast-xml-parser": "^5.2.5",
170
170
  "formidable": "3.5.4",
171
171
  "highlight.js": "11.11.1",
172
172
  "html-to-image": "1.11.13",
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { memo, useEffect, useMemo, useRef } from 'react';
3
+ import { memo, useEffect, useMemo, useRef, useState } from 'react';
4
4
  import { createRoot, type Root } from 'react-dom/client';
5
5
  import type { string_markdown } from '../../../types/string_markdown';
6
6
  import { classNames } from '../../_common/react-utils/classNames';
@@ -23,8 +23,37 @@ type MarkdownContentProps = {
23
23
 
24
24
  className?: string;
25
25
  onCreateAgent?: (bookContent: string) => void;
26
+ theme?: 'LIGHT' | 'DARK';
26
27
  };
27
28
 
29
+ /**
30
+ * Visual theme consumed by nested code-block renderers.
31
+ *
32
+ * @private utility of `MarkdownContent` component
33
+ */
34
+ type MarkdownContentTheme = NonNullable<MarkdownContentProps['theme']>;
35
+
36
+ /**
37
+ * Resolves the active document theme when a host application does not pass an explicit theme.
38
+ *
39
+ * @private utility of `MarkdownContent` component
40
+ */
41
+ function resolveMarkdownContentTheme(explicitTheme?: MarkdownContentTheme): MarkdownContentTheme {
42
+ if (explicitTheme) {
43
+ return explicitTheme;
44
+ }
45
+
46
+ if (typeof document !== 'undefined') {
47
+ const resolvedTheme = document.documentElement.dataset.themeResolved;
48
+
49
+ if (resolvedTheme === 'dark' || document.documentElement.classList.contains('dark')) {
50
+ return 'DARK';
51
+ }
52
+ }
53
+
54
+ return 'LIGHT';
55
+ }
56
+
28
57
  /**
29
58
  * Returns a stable key for a `<details>` element based on its `<summary>` text.
30
59
  * Used to identify and restore open state across re-renders.
@@ -85,7 +114,7 @@ function resolveClickedDetailsElement(target: EventTarget | null, container: HTM
85
114
  * @public exported from `@promptbook/components`
86
115
  */
87
116
  export const MarkdownContent = memo(function MarkdownContent(props: MarkdownContentProps) {
88
- const { content, className, onCreateAgent } = props;
117
+ const { content, className, onCreateAgent, theme } = props;
89
118
  const htmlContent = useMemo(
90
119
  () =>
91
120
  renderMarkdown(content, {
@@ -93,6 +122,9 @@ export const MarkdownContent = memo(function MarkdownContent(props: MarkdownCont
93
122
  }),
94
123
  [content],
95
124
  );
125
+ const [resolvedTheme, setResolvedTheme] = useState<MarkdownContentTheme>(() =>
126
+ resolveMarkdownContentTheme(theme),
127
+ );
96
128
  const containerRef = useRef<HTMLDivElement>(null);
97
129
  const rootsRef = useRef<Root[]>([]);
98
130
  /** Tracks which `<details>` elements (by summary key) are currently open */
@@ -100,6 +132,28 @@ export const MarkdownContent = memo(function MarkdownContent(props: MarkdownCont
100
132
  const onCreateAgentRef = useRef(onCreateAgent);
101
133
  onCreateAgentRef.current = onCreateAgent;
102
134
 
135
+ useEffect(() => {
136
+ if (theme) {
137
+ setResolvedTheme(theme);
138
+ return;
139
+ }
140
+
141
+ const updateTheme = () => setResolvedTheme(resolveMarkdownContentTheme());
142
+ updateTheme();
143
+
144
+ if (typeof document === 'undefined' || typeof MutationObserver === 'undefined') {
145
+ return;
146
+ }
147
+
148
+ const observer = new MutationObserver(updateTheme);
149
+ observer.observe(document.documentElement, {
150
+ attributes: true,
151
+ attributeFilter: ['class', 'data-theme-resolved'],
152
+ });
153
+
154
+ return () => observer.disconnect();
155
+ }, [theme]);
156
+
103
157
  useEffect(() => {
104
158
  // Cleanup previous roots
105
159
  rootsRef.current.forEach((root) => root.unmount());
@@ -191,7 +245,14 @@ export const MarkdownContent = memo(function MarkdownContent(props: MarkdownCont
191
245
 
192
246
  // Render CodeBlock
193
247
  const root = createRoot(mountPoint);
194
- root.render(<CodeBlock code={code} language={language} onCreateAgent={onCreateAgentRef.current} />);
248
+ root.render(
249
+ <CodeBlock
250
+ code={code}
251
+ language={language}
252
+ onCreateAgent={onCreateAgentRef.current}
253
+ theme={resolvedTheme}
254
+ />,
255
+ );
195
256
  rootsRef.current.push(root);
196
257
  });
197
258
 
@@ -203,7 +264,7 @@ export const MarkdownContent = memo(function MarkdownContent(props: MarkdownCont
203
264
  rootsRef.current.forEach((root) => root.unmount());
204
265
  rootsRef.current = [];
205
266
  };
206
- }, [htmlContent]);
267
+ }, [htmlContent, resolvedTheme]);
207
268
 
208
269
  return (
209
270
  <div