@promptbook/cli 0.112.0-93 → 0.112.0-95

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 (40) hide show
  1. package/apps/agents-server/src/app/admin/_components/AdminConfigurationShell.tsx +13 -7
  2. package/apps/agents-server/src/app/admin/code-runners/CodeRunnersClient.tsx +225 -0
  3. package/apps/agents-server/src/app/admin/code-runners/page.tsx +14 -0
  4. package/apps/agents-server/src/app/admin/environment/EnvironmentVariablesClient.tsx +259 -0
  5. package/apps/agents-server/src/app/admin/environment/page.tsx +21 -0
  6. package/apps/agents-server/src/app/admin/logs/LogsClient.tsx +78 -0
  7. package/apps/agents-server/src/app/admin/logs/page.tsx +14 -0
  8. package/apps/agents-server/src/app/admin/servers/ServersClient.tsx +64 -33
  9. package/apps/agents-server/src/app/admin/servers/ServersRegistryApi.ts +5 -0
  10. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +15 -2
  11. package/apps/agents-server/src/app/admin/servers/page.tsx +3 -3
  12. package/apps/agents-server/src/app/admin/servers/useServersRegistryState.ts +12 -2
  13. package/apps/agents-server/src/app/api/admin/code-runners/route.ts +104 -0
  14. package/apps/agents-server/src/app/api/admin/environment/route.ts +65 -0
  15. package/apps/agents-server/src/app/api/admin/logs/route.ts +24 -0
  16. package/apps/agents-server/src/app/api/admin/servers/[serverId]/route.ts +79 -3
  17. package/apps/agents-server/src/app/api/admin/servers/route.ts +36 -1
  18. package/apps/agents-server/src/app/page.tsx +101 -1
  19. package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +23 -4
  20. package/apps/agents-server/src/languages/ServerTranslationKeys.ts +4 -0
  21. package/apps/agents-server/src/languages/translations/czech.yaml +4 -0
  22. package/apps/agents-server/src/languages/translations/english.yaml +4 -0
  23. package/apps/agents-server/src/tools/$provideServer.ts +27 -0
  24. package/apps/agents-server/src/utils/serverRegistry.ts +20 -1
  25. package/apps/agents-server/src/utils/session.ts +123 -2
  26. package/apps/agents-server/src/utils/vpsConfiguration.ts +550 -0
  27. package/esm/index.es.js +1 -1
  28. package/esm/index.es.js.map +1 -1
  29. package/esm/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
  30. package/esm/src/version.d.ts +1 -1
  31. package/package.json +2 -1
  32. package/src/book-components/Chat/MarkdownContent/MarkdownContent.tsx +9 -398
  33. package/src/book-components/Chat/utils/renderMarkdown.ts +323 -8
  34. package/src/other/templates/getTemplatesPipelineCollection.ts +683 -879
  35. package/src/version.ts +2 -2
  36. package/src/versions.txt +2 -0
  37. package/umd/index.umd.js +1 -1
  38. package/umd/index.umd.js.map +1 -1
  39. package/umd/src/book-components/Chat/utils/renderMarkdown.test.d.ts +1 -0
  40. package/umd/src/version.d.ts +1 -1
@@ -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-92`).
18
+ * It follows semantic versioning (e.g., `0.112.0-94`).
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-93",
3
+ "version": "0.112.0-95",
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,
@@ -161,6 +161,7 @@
161
161
  "configchecker": "1.5.132",
162
162
  "cross-fetch": "4.0.0",
163
163
  "destroyable": "0.12.145",
164
+ "dompurify": "3.4.6",
164
165
  "formidable": "3.5.4",
165
166
  "highlight.js": "11.11.1",
166
167
  "html-to-image": "1.11.13",
@@ -1,195 +1,13 @@
1
1
  'use client';
2
2
 
3
- import katex from 'katex';
4
3
  import { memo, useEffect, useMemo, useRef } from 'react';
5
4
  import { createRoot, type Root } from 'react-dom/client';
6
- import { Converter as ShowdownConverter } from 'showdown';
7
- import type { string_html, string_markdown } from '../../../types/string_markdown';
8
- import { TODO_USE } from '../../../utils/organization/TODO_USE';
5
+ import type { string_markdown } from '../../../types/string_markdown';
9
6
  import { classNames } from '../../_common/react-utils/classNames';
10
7
  import { CodeBlock } from '../CodeBlock/CodeBlock';
11
- import { createCitationMarkerRegex, parseCitationMarker } from '../utils/parseCitationMarker';
8
+ import { renderMarkdown } from '../utils/renderMarkdown';
12
9
  import styles from './MarkdownContent.module.css';
13
10
 
14
- /**
15
- * Normalizes markdown sublists so they render correctly under ordered list items.
16
- *
17
- * @param markdown - Markdown content to normalize.
18
- * @returns Markdown with normalized sublist indentation.
19
- *
20
- * @private utility of `MarkdownContent` component
21
- */
22
- function normalizeMarkdownSublists(markdown: string_markdown): string_markdown {
23
- const lines = markdown.split(/\r?\n/);
24
- let orderedIndent: number | null = null;
25
- let shouldIndentUnordered = false;
26
- let activeFence: '```' | '~~~' | null = null;
27
-
28
- const normalizedLines = lines.map((line) => {
29
- const trimmedLine = line.trim();
30
-
31
- const fenceMatch = trimmedLine.match(/^(```|~~~)/);
32
- if (fenceMatch) {
33
- const fence = fenceMatch[1] as '```' | '~~~';
34
- activeFence = activeFence === fence ? null : fence;
35
- return line;
36
- }
37
-
38
- if (activeFence) {
39
- return line;
40
- }
41
-
42
- if (trimmedLine === '') {
43
- return line;
44
- }
45
-
46
- const orderedMatch = line.match(/^(\s*)(\d+)\.\s+/);
47
- if (orderedMatch) {
48
- orderedIndent = orderedMatch[1]!.length;
49
- shouldIndentUnordered = true;
50
- return line;
51
- }
52
-
53
- const unorderedMatch = line.match(/^(\s*)([-+*])\s+/);
54
- if (unorderedMatch) {
55
- if (shouldIndentUnordered && orderedIndent !== null) {
56
- const currentIndent = unorderedMatch[1]!.length;
57
- const targetIndent = orderedIndent + 4;
58
-
59
- if (currentIndent < targetIndent) {
60
- return `${' '.repeat(targetIndent)}${line.trimStart()}`;
61
- }
62
- }
63
-
64
- return line;
65
- }
66
-
67
- orderedIndent = null;
68
- shouldIndentUnordered = false;
69
-
70
- return line;
71
- });
72
-
73
- return normalizedLines.join('\n') as string_markdown;
74
- }
75
-
76
- /**
77
- * Creates a showdown converter configured for chat markdown rendering
78
- *
79
- * @private utility of `MarkdownContent` component
80
- */
81
- function createChatMarkdownConverter(): ShowdownConverter {
82
- return new ShowdownConverter({
83
- flavor: 'github',
84
- tables: true,
85
- strikethrough: true,
86
- tasklists: true,
87
- ghCodeBlocks: true,
88
- ghMentions: false,
89
- ghMentionsLink: '',
90
- openLinksInNewWindow: true,
91
- backslashEscapesHTMLTags: true,
92
- emoji: true,
93
- underline: true,
94
- completeHTMLDocument: false,
95
- metadata: false,
96
- splitAdjacentBlockquotes: true,
97
- noHeaderId: true,
98
- headerLevelStart: 1,
99
- parseImgDimensions: true,
100
- simplifiedAutoLink: true,
101
- literalMidWordUnderscores: true,
102
- literalMidWordAsterisks: false,
103
- simpleLineBreaks: true,
104
- requireSpaceBeforeHeadingText: true,
105
- ghCompatibleHeaderId: true,
106
- prefixHeaderId: 'chat-header-',
107
- rawPrefixHeaderId: false,
108
- rawHeaderId: false,
109
- smoothLivePreview: true,
110
- smartIndentationFix: true,
111
- disableForced4SpacesIndentedSublists: false,
112
- encodeEmails: true,
113
- extensions: [
114
- () => ({
115
- type: 'lang',
116
- regex: createCitationMarkerRegex(),
117
- replace: (match: string) => {
118
- const citationMarker = parseCitationMarker(match);
119
- if (!citationMarker) {
120
- return match;
121
- }
122
-
123
- TODO_USE(citationMarker.source);
124
-
125
- // Note: Citations are now rendered as chips in ChatMessageItem
126
- // We replace them with numbered superscript references
127
- return `<sup class="${styles.citationRef}">[${citationMarker.id}]</sup>`;
128
- },
129
- }),
130
- ],
131
- });
132
- }
133
-
134
- /**
135
- * Pre-configured showdown converter for chat markdown
136
- *
137
- * @private utility of `MarkdownContent` component
138
- */
139
- const chatMarkdownConverter = createChatMarkdownConverter();
140
-
141
- /**
142
- * Definition of math delimiter.
143
- */
144
- type MathDelimiterDefinition = {
145
- regex: RegExp;
146
- displayMode: boolean;
147
- };
148
-
149
- /**
150
- * Collection of math delimiter definitions.
151
- */
152
- const mathDelimiterDefinitions: ReadonlyArray<MathDelimiterDefinition> = [
153
- { regex: /(^|[^\\])\$\$([\s\S]+?)\$\$/g, displayMode: true },
154
- { regex: /(^|[^\\])\\\[([\s\S]+?)\\\]/g, displayMode: true },
155
- { regex: /(^|[^\\])\\\(([\s\S]+?)\\\)/g, displayMode: false },
156
- { regex: /(^|[^\\])\$([^$\n]+?)\$/g, displayMode: false },
157
- ];
158
-
159
- /**
160
- * Pattern matching CODE FENCE.
161
- */
162
- const CODE_FENCE_REGEX = /(`{3,}|~{3,})(?:[^\n\r]*)\r?\n[\s\S]*?\r?\n\1[^\n\r]*/g;
163
- /**
164
- * Pattern matching INLINE CODE.
165
- */
166
- const INLINE_CODE_REGEX = /(`+)([\s\S]*?)(\1)/g;
167
- /**
168
- * Prefix for CODE PLACEHOLDER.
169
- */
170
- const CODE_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_CODE_PLACEHOLDER__';
171
- /**
172
- * Pattern matching CODE PLACEHOLDER.
173
- */
174
- const CODE_PLACEHOLDER_REGEX = new RegExp(`${CODE_PLACEHOLDER_PREFIX}(\\d+)__`, 'g');
175
-
176
- /**
177
- * Pattern matching DETAILS BLOCK.
178
- */
179
- const DETAILS_BLOCK_REGEX = /<details[\s\S]*?<\/details\s*>/gi;
180
- /**
181
- * Prefix for DETAILS PLACEHOLDER.
182
- */
183
- const DETAILS_PLACEHOLDER_PREFIX = '@@PROMPTBOOK_DETAILS_PLACEHOLDER__';
184
- /**
185
- * Pattern matching DETAILS PLACEHOLDER.
186
- */
187
- const DETAILS_PLACEHOLDER_REGEX = new RegExp(`${DETAILS_PLACEHOLDER_PREFIX}(\\d+)__`, 'g');
188
- /**
189
- * Matches a Showdown-wrapped placeholder such as `<p>@@PROMPTBOOK_DETAILS_PLACEHOLDER__0__</p>`
190
- */
191
- const DETAILS_PLACEHOLDER_WRAPPED_REGEX = new RegExp(`<p>\\s*(${DETAILS_PLACEHOLDER_PREFIX}\\d+__)\\s*<\\/p>`, 'g');
192
-
193
11
  /**
194
12
  * Selector used by the delegated summary click handler.
195
13
  *
@@ -197,219 +15,6 @@ const DETAILS_PLACEHOLDER_WRAPPED_REGEX = new RegExp(`<p>\\s*(${DETAILS_PLACEHOL
197
15
  */
198
16
  const DETAILS_SUMMARY_SELECTOR = 'summary';
199
17
 
200
- /**
201
- * Result of masked code segments.
202
- */
203
- type MaskedCodeSegmentsResult = {
204
- masked: string_markdown;
205
- restore: (value: string_markdown) => string_markdown;
206
- };
207
-
208
- /**
209
- * Result of masked details blocks.
210
- */
211
- type MaskedDetailsBlocksResult = {
212
- masked: string_markdown;
213
- restore: (value: string_html) => string_html;
214
- };
215
-
216
- /**
217
- * Renders the body of one raw `<details>` block as markdown while keeping the
218
- * outer `<details>` and optional `<summary>` markup untouched.
219
- *
220
- * @param detailsBlock - Raw `<details>...</details>` HTML captured from markdown.
221
- * @returns `<details>` HTML whose body has been converted from markdown to HTML.
222
- *
223
- * @private utility of `MarkdownContent` component
224
- */
225
- function renderDetailsBlock(detailsBlock: string): string_html {
226
- const openTagMatch = detailsBlock.match(/^<details\b[^>]*>/i);
227
- const closeTagMatch = detailsBlock.match(/<\/details\s*>$/i);
228
-
229
- if (!openTagMatch || !closeTagMatch) {
230
- return detailsBlock as string_html;
231
- }
232
-
233
- const openTag = openTagMatch[0];
234
- const closeTag = closeTagMatch[0];
235
- const innerContent = detailsBlock.slice(openTag.length, detailsBlock.length - closeTag.length);
236
- const summaryMatch = innerContent.match(/^(\s*<summary\b[^>]*>[\s\S]*?<\/summary\s*>)([\s\S]*)$/i);
237
- const summaryHtml = summaryMatch?.[1] ?? '';
238
- const bodyMarkdown = (summaryMatch?.[2] ?? innerContent) as string_markdown;
239
- const renderedBody = renderMarkdown(bodyMarkdown);
240
-
241
- return `${openTag}${summaryHtml}${renderedBody}${closeTag}` as string_html;
242
- }
243
-
244
- /**
245
- * Masks inline and fenced code segments so math rendering never touches them.
246
- *
247
- * @param markdown - Markdown text to mask.
248
- * @returns Masked markdown and a restore helper.
249
- *
250
- * @private utility of `MarkdownContent` component
251
- */
252
- function maskMarkdownCodeSegments(markdown: string_markdown): MaskedCodeSegmentsResult {
253
- const segments: string[] = [];
254
- let masked = markdown;
255
-
256
- const addPlaceholder = (segment: string) => {
257
- const placeholder = `${CODE_PLACEHOLDER_PREFIX}${segments.length}__`;
258
- segments.push(segment);
259
- return placeholder;
260
- };
261
-
262
- const maskWith = (regex: RegExp) => {
263
- regex.lastIndex = 0;
264
- masked = masked.replace(regex, (match) => addPlaceholder(match));
265
- };
266
-
267
- maskWith(CODE_FENCE_REGEX);
268
- maskWith(INLINE_CODE_REGEX);
269
-
270
- return {
271
- masked: masked as string_markdown,
272
- restore(value: string_markdown): string_markdown {
273
- return value.replace(CODE_PLACEHOLDER_REGEX, (_match, index) => segments[Number(index)] ?? '');
274
- },
275
- };
276
- }
277
-
278
- /**
279
- * Masks `<details>…</details>` blocks in the markdown source so that Showdown never
280
- * processes their content (which would break them with `simpleLineBreaks: true`).
281
- *
282
- * The original blocks are replaced with placeholders before Showdown runs, then restored
283
- * afterwards with their non-summary body converted through the normal markdown pipeline.
284
- * Any surrounding `<p>` wrapper that Showdown may have injected around a placeholder is
285
- * stripped so the `<details>` element remains a proper block-level element.
286
- *
287
- * @param markdown - Markdown text that may contain raw HTML `<details>` blocks.
288
- * @returns Masked markdown and a restore helper that returns `string_html`.
289
- *
290
- * @private utility of `MarkdownContent` component
291
- */
292
- function maskDetailsBlocks(markdown: string_markdown): MaskedDetailsBlocksResult {
293
- const blocks: string_html[] = [];
294
-
295
- DETAILS_BLOCK_REGEX.lastIndex = 0;
296
- const masked = markdown.replace(DETAILS_BLOCK_REGEX, (match) => {
297
- const placeholder = `${DETAILS_PLACEHOLDER_PREFIX}${blocks.length}__`;
298
- blocks.push(renderDetailsBlock(match));
299
- return placeholder;
300
- }) as string_markdown;
301
-
302
- return {
303
- masked,
304
- restore(value: string_html): string_html {
305
- return value
306
- .replace(DETAILS_PLACEHOLDER_WRAPPED_REGEX, '$1')
307
- .replace(DETAILS_PLACEHOLDER_REGEX, (_match, index) => blocks[Number(index)] ?? '') as string_html;
308
- },
309
- };
310
- }
311
-
312
- /**
313
- * Replaces math delimiter.
314
- */
315
- function replaceMathDelimiter(md: string, delimiter: MathDelimiterDefinition): string {
316
- return md.replace(delimiter.regex, (...args) => {
317
- const match = args[0] ?? '';
318
- const prefix = args[1] ?? '';
319
- const math = args[2] ?? '';
320
-
321
- if (!math) {
322
- return match;
323
- }
324
-
325
- try {
326
- const rendered = katex.renderToString(math, {
327
- displayMode: delimiter.displayMode,
328
- throwOnError: false,
329
- });
330
- return `${prefix}${rendered}`;
331
- } catch {
332
- return match;
333
- }
334
- });
335
- }
336
-
337
- /**
338
- * Renders math expressions in markdown using KaTeX for the supported delimiter pairs.
339
- *
340
- * Supported delimiters: `$$...$$`, `\[...\]`, `\(...\)`, and `$...$`.
341
- *
342
- * @private utility of `MarkdownContent` component
343
- */
344
- function renderMathInMarkdown(md: string): string {
345
- const { masked, restore } = maskMarkdownCodeSegments(md);
346
- let processed = masked;
347
-
348
- for (const delimiter of mathDelimiterDefinitions) {
349
- processed = replaceMathDelimiter(processed, delimiter);
350
- }
351
- processed = processed.replace(/\\$/g, '$');
352
- return restore(processed);
353
- }
354
-
355
- /**
356
- * Convert markdown content to HTML
357
- *
358
- * TODO: [🧠] Maybe export from `@promptbook/markdown-utils`
359
- *
360
- * @param markdown - The markdown content to convert
361
- * @returns HTML string ready for rendering
362
- *
363
- * @public exported from `@promptbook/components`
364
- */
365
- function renderMarkdown(markdown: string_markdown): string_html {
366
- if (!markdown || typeof markdown !== 'string') {
367
- return '' as string_html;
368
- }
369
-
370
- try {
371
- const normalizedMarkdown = normalizeMarkdownSublists(markdown);
372
- const { masked: maskedMarkdown, restore: restoreDetails } = maskDetailsBlocks(normalizedMarkdown);
373
- const processedMarkdown = renderMathInMarkdown(maskedMarkdown);
374
- const html = chatMarkdownConverter.makeHtml(processedMarkdown);
375
-
376
- if (typeof window !== 'undefined') {
377
- if (html.match(/class="katex/)) {
378
- const katexCssId = 'katex-css';
379
- if (!window.document.getElementById(katexCssId)) {
380
- const link = window.document.createElement('link');
381
- link.id = katexCssId;
382
- link.rel = 'stylesheet';
383
- link.href = 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css';
384
- window.document.head.appendChild(link);
385
- }
386
- }
387
- }
388
-
389
- const restoredHtml = restoreDetails(html as string_html);
390
- const sanitizedHtml = restoredHtml
391
- .replace(/<\s*(script|style|iframe|object|embed)[^>]*>[\s\S]*?<\s*\/\s*\1\s*>/gi, '')
392
- .replace(/\s+on\w+="[^"]*"/gi, '')
393
- .replace(/\s+javascript:/gi, '')
394
- .replace(/\s+data:/gi, '')
395
- .replace(/\s+vbscript:/gi, '');
396
-
397
- return sanitizedHtml as string_html;
398
- } catch (error) {
399
- console.error('Error rendering markdown:', error);
400
- return markdown.replace(/[<>&"']/g, (char) => {
401
- const entities: Record<string, string> = {
402
- '<': '<',
403
- '>': '>',
404
- '&': '&',
405
- '"': '"',
406
- "'": '&#39;',
407
- };
408
- return entities[char] || char;
409
- }) as string_html;
410
- }
411
- }
412
-
413
18
  /**
414
19
  * Props for markdown content.
415
20
  */
@@ -481,7 +86,13 @@ function resolveClickedDetailsElement(target: EventTarget | null, container: HTM
481
86
  */
482
87
  export const MarkdownContent = memo(function MarkdownContent(props: MarkdownContentProps) {
483
88
  const { content, className, onCreateAgent } = props;
484
- const htmlContent = useMemo(() => renderMarkdown(content), [content]);
89
+ const htmlContent = useMemo(
90
+ () =>
91
+ renderMarkdown(content, {
92
+ citationReferenceClassName: styles.citationRef,
93
+ }),
94
+ [content],
95
+ );
485
96
  const containerRef = useRef<HTMLDivElement>(null);
486
97
  const rootsRef = useRef<Root[]>([]);
487
98
  /** Tracks which `<details>` elements (by summary key) are currently open */