@promptbook/cli 0.113.0-0 → 0.113.0-2

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 (35) hide show
  1. package/README.md +1 -17
  2. package/agents/default/developer.book +3 -0
  3. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +3 -3
  4. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +11 -2
  5. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +5 -5
  6. package/apps/agents-server/src/app/api/page-preview/input/route.ts +161 -0
  7. package/apps/agents-server/src/app/api/page-preview/stream/route.ts +81 -0
  8. package/apps/agents-server/src/components/Header/HeaderHomepageLink.tsx +1 -1
  9. package/apps/agents-server/src/constants/chatVisualMode.ts +1 -1
  10. package/apps/agents-server/src/constants/themeMode.ts +1 -1
  11. package/apps/agents-server/src/database/migrations/2026-05-2600-default-theme.sql +2 -2
  12. package/apps/agents-server/src/utils/chatTasksAdmin.ts +4 -1
  13. package/apps/agents-server/src/utils/createPagePreviewBrowserStream.ts +179 -0
  14. package/apps/agents-server/src/utils/getAdminChatTasksResponse/mapVpsSelfUpdateJobToAdminChatTask.ts +84 -0
  15. package/apps/agents-server/src/utils/getAdminChatTasksResponse.ts +221 -5
  16. package/apps/agents-server/src/utils/pagePreviewBrowserSessions.ts +234 -0
  17. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +12 -0
  18. package/esm/index.es.js +26 -5
  19. package/esm/index.es.js.map +1 -1
  20. package/esm/src/book-components/Chat/Chat/CitationIframePreview.d.ts +2 -2
  21. package/esm/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
  22. package/esm/src/version.d.ts +1 -1
  23. package/package.json +1 -1
  24. package/src/book-components/Chat/Chat/Chat.module.css +37 -0
  25. package/src/book-components/Chat/Chat/CitationIframePreview.tsx +138 -11
  26. package/src/book-components/Chat/Chat/createProgressCardChecklistMarkdown.ts +1 -8
  27. package/src/cli/promptbookCli.ts +28 -3
  28. package/src/other/templates/getTemplatesPipelineCollection.ts +750 -812
  29. package/src/version.ts +2 -2
  30. package/src/versions.txt +2 -0
  31. package/umd/index.umd.js +26 -5
  32. package/umd/index.umd.js.map +1 -1
  33. package/umd/src/book-components/Chat/Chat/CitationIframePreview.d.ts +2 -2
  34. package/umd/src/book-components/Chat/Chat/CitationIframePreview.test.d.ts +2 -0
  35. package/umd/src/version.d.ts +1 -1
@@ -9,8 +9,8 @@ export type CitationIframePreviewProps = {
9
9
  };
10
10
  /**
11
11
  * Renders a citation URL preview as an iframe when the target page allows embedding,
12
- * or falls back to a server-side screenshot with an "Open in new tab" link when it
13
- * does not (e.g. X-Frame-Options: DENY / SAMEORIGIN).
12
+ * or falls back to a live server-side browser stream with an "Open in new tab" link
13
+ * when it does not (e.g. X-Frame-Options: DENY / SAMEORIGIN).
14
14
  *
15
15
  * Embedding capability is determined by `GET /api/page-preview/check?url=<url>`.
16
16
  * If that endpoint is unavailable the component falls back to the iframe directly.
@@ -0,0 +1,2 @@
1
+ /** @jest-environment jsdom */
2
+ export {};
@@ -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`).
18
+ * It follows semantic versioning (e.g., `0.113.0-1`).
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.113.0-0",
3
+ "version": "0.113.0-2",
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,
@@ -3356,6 +3356,43 @@
3356
3356
  text-decoration: underline;
3357
3357
  }
3358
3358
 
3359
+ .citationBrowserStreamFallback {
3360
+ width: 100%;
3361
+ position: relative;
3362
+ display: flex;
3363
+ flex-direction: column;
3364
+ }
3365
+
3366
+ .citationBrowserStreamImage {
3367
+ width: 100%;
3368
+ max-height: 60vh;
3369
+ min-height: 400px;
3370
+ object-fit: cover;
3371
+ object-position: top;
3372
+ display: block;
3373
+ cursor: crosshair;
3374
+ user-select: none;
3375
+ touch-action: none;
3376
+ }
3377
+
3378
+ .citationBrowserStreamLink {
3379
+ display: block;
3380
+ text-align: center;
3381
+ padding: 10px 16px;
3382
+ background: #f0f0f0;
3383
+ color: #333;
3384
+ font-size: 0.9em;
3385
+ font-weight: 500;
3386
+ text-decoration: none;
3387
+ border-top: 1px solid #ddd;
3388
+ transition: background 0.15s;
3389
+ }
3390
+
3391
+ .citationBrowserStreamLink:hover {
3392
+ background: #e0e0e0;
3393
+ text-decoration: underline;
3394
+ }
3395
+
3359
3396
  .citationMetadata {
3360
3397
  background: #f8f8f8;
3361
3398
  padding: 16px;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
3
+ import { useEffect, useMemo, useState, type MouseEvent, type WheelEvent } from 'react';
4
4
  import styles from './Chat.module.css';
5
5
 
6
6
  /**
@@ -18,12 +18,36 @@ export type CitationIframePreviewProps = {
18
18
  *
19
19
  * @private type of `<CitationIframePreview/>`
20
20
  */
21
- type EmbedStatus = 'loading' | 'embed' | 'screenshot';
21
+ type EmbedStatus = 'loading' | 'embed' | 'browser-stream';
22
+
23
+ /**
24
+ * Prefix shared with the Agents Server page-preview stream route.
25
+ *
26
+ * @private component constant of `<CitationIframePreview/>`
27
+ */
28
+ const BROWSER_PREVIEW_SESSION_ID_PREFIX = 'page-preview-';
29
+
30
+ /**
31
+ * Fallback suffix length used when `crypto.randomUUID` is unavailable.
32
+ *
33
+ * @private component constant of `<CitationIframePreview/>`
34
+ */
35
+ const BROWSER_PREVIEW_RANDOM_SUFFIX_LENGTH = 24;
36
+
37
+ /**
38
+ * Pointer ratio resolved from one browser preview interaction.
39
+ *
40
+ * @private type of `<CitationIframePreview/>`
41
+ */
42
+ type BrowserPreviewPointerRatio = {
43
+ readonly xRatio: number;
44
+ readonly yRatio: number;
45
+ };
22
46
 
23
47
  /**
24
48
  * Renders a citation URL preview as an iframe when the target page allows embedding,
25
- * or falls back to a server-side screenshot with an "Open in new tab" link when it
26
- * does not (e.g. X-Frame-Options: DENY / SAMEORIGIN).
49
+ * or falls back to a live server-side browser stream with an "Open in new tab" link
50
+ * when it does not (e.g. X-Frame-Options: DENY / SAMEORIGIN).
27
51
  *
28
52
  * Embedding capability is determined by `GET /api/page-preview/check?url=<url>`.
29
53
  * If that endpoint is unavailable the component falls back to the iframe directly.
@@ -32,6 +56,7 @@ type EmbedStatus = 'loading' | 'embed' | 'screenshot';
32
56
  */
33
57
  export function CitationIframePreview({ src, title }: CitationIframePreviewProps) {
34
58
  const [status, setStatus] = useState<EmbedStatus>('loading');
59
+ const browserPreviewSessionId = useMemo(() => createBrowserPreviewSessionId(), [src]);
35
60
 
36
61
  useEffect(() => {
37
62
  let cancelled = false;
@@ -40,7 +65,7 @@ export function CitationIframePreview({ src, title }: CitationIframePreviewProps
40
65
  .then((response) => response.json())
41
66
  .then((data: { canEmbed: boolean }) => {
42
67
  if (!cancelled) {
43
- setStatus(data.canEmbed ? 'embed' : 'screenshot');
68
+ setStatus(data.canEmbed ? 'embed' : 'browser-stream');
44
69
  }
45
70
  })
46
71
  .catch(() => {
@@ -59,15 +84,45 @@ export function CitationIframePreview({ src, title }: CitationIframePreviewProps
59
84
  return <div className={styles.citationIframeLoading}>Loading preview…</div>;
60
85
  }
61
86
 
62
- if (status === 'screenshot') {
87
+ if (status === 'browser-stream') {
63
88
  return (
64
- <div className={styles.citationScreenshotFallback}>
89
+ <div className={styles.citationBrowserStreamFallback}>
65
90
  <img
66
- src={`/api/page-preview/screenshot?url=${encodeURIComponent(src)}`}
67
- alt={`Screenshot of ${title}`}
68
- className={styles.citationScreenshotImage}
91
+ src={`/api/page-preview/stream?url=${encodeURIComponent(src)}&sessionId=${encodeURIComponent(
92
+ browserPreviewSessionId,
93
+ )}`}
94
+ alt={`Live browser preview of ${title}`}
95
+ className={styles.citationBrowserStreamImage}
96
+ draggable={false}
97
+ onClick={(event) => {
98
+ const pointerRatio = resolveBrowserPreviewPointerRatio(event);
99
+ if (!pointerRatio) {
100
+ return;
101
+ }
102
+
103
+ sendBrowserPreviewInput({
104
+ sessionId: browserPreviewSessionId,
105
+ type: 'click',
106
+ ...pointerRatio,
107
+ });
108
+ }}
109
+ onWheel={(event) => {
110
+ event.preventDefault();
111
+ const pointerRatio = resolveBrowserPreviewPointerRatio(event);
112
+ if (!pointerRatio) {
113
+ return;
114
+ }
115
+
116
+ sendBrowserPreviewInput({
117
+ sessionId: browserPreviewSessionId,
118
+ type: 'wheel',
119
+ deltaX: event.deltaX,
120
+ deltaY: event.deltaY,
121
+ ...pointerRatio,
122
+ });
123
+ }}
69
124
  />
70
- <a href={src} target="_blank" rel="noopener noreferrer" className={styles.citationScreenshotLink}>
125
+ <a href={src} target="_blank" rel="noopener noreferrer" className={styles.citationBrowserStreamLink}>
71
126
  Open in new tab ↗
72
127
  </a>
73
128
  </div>
@@ -76,3 +131,75 @@ export function CitationIframePreview({ src, title }: CitationIframePreviewProps
76
131
 
77
132
  return <iframe src={src} className={styles.citationIframe} title={title} />;
78
133
  }
134
+
135
+ /**
136
+ * Creates one client-side browser preview session id.
137
+ *
138
+ * @returns Session id accepted by Agents Server page-preview stream routes.
139
+ */
140
+ function createBrowserPreviewSessionId(): string {
141
+ const randomId =
142
+ globalThis.crypto && 'randomUUID' in globalThis.crypto
143
+ ? globalThis.crypto.randomUUID()
144
+ : createFallbackBrowserPreviewSessionSuffix();
145
+
146
+ return `${BROWSER_PREVIEW_SESSION_ID_PREFIX}${randomId}`.toLowerCase();
147
+ }
148
+
149
+ /**
150
+ * Creates a sufficiently long fallback session suffix when Web Crypto UUIDs are unavailable.
151
+ *
152
+ * @returns Lowercase random suffix.
153
+ */
154
+ function createFallbackBrowserPreviewSessionSuffix(): string {
155
+ let suffix = '';
156
+
157
+ while (suffix.length < BROWSER_PREVIEW_RANDOM_SUFFIX_LENGTH) {
158
+ suffix += Math.random().toString(36).slice(2);
159
+ }
160
+
161
+ return suffix.slice(0, BROWSER_PREVIEW_RANDOM_SUFFIX_LENGTH);
162
+ }
163
+
164
+ /**
165
+ * Resolves pointer coordinates as ratios inside the streamed preview image.
166
+ *
167
+ * @param event - Mouse or wheel event fired inside the live preview image.
168
+ * @returns Pointer ratio or `null` when the image has no measurable size.
169
+ */
170
+ function resolveBrowserPreviewPointerRatio(
171
+ event: MouseEvent<HTMLImageElement> | WheelEvent<HTMLImageElement>,
172
+ ): BrowserPreviewPointerRatio | null {
173
+ const rect = event.currentTarget.getBoundingClientRect();
174
+ if (rect.width <= 0 || rect.height <= 0) {
175
+ return null;
176
+ }
177
+
178
+ return {
179
+ xRatio: (event.clientX - rect.left) / rect.width,
180
+ yRatio: (event.clientY - rect.top) / rect.height,
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Sends one browser-preview interaction to the Agents Server stream session.
186
+ *
187
+ * @param payload - Browser-preview input payload.
188
+ */
189
+ function sendBrowserPreviewInput(payload: {
190
+ readonly sessionId: string;
191
+ readonly type: 'click' | 'wheel';
192
+ readonly xRatio: number;
193
+ readonly yRatio: number;
194
+ readonly deltaX?: number;
195
+ readonly deltaY?: number;
196
+ }): void {
197
+ void fetch('/api/page-preview/input', {
198
+ method: 'POST',
199
+ headers: {
200
+ 'Content-Type': 'application/json',
201
+ },
202
+ body: JSON.stringify(payload),
203
+ keepalive: true,
204
+ }).catch(() => undefined);
205
+ }
@@ -14,13 +14,6 @@ const PENDING_PROGRESS_MARKER_HTML = '<span data-chat-progress-marker="pending"
14
14
  */
15
15
  const COMPLETED_PROGRESS_MARKER_HTML = '<span data-chat-progress-marker="completed" aria-hidden="true"></span>';
16
16
 
17
- /**
18
- * Human-facing label shown for "now" progress updates.
19
- *
20
- * @private internal helper of `<ChatMessageItem/>`
21
- */
22
- const PROGRESS_NOW_LABEL = "What I'm Doing Now";
23
-
24
17
  /**
25
18
  * Human-facing label shown for "next" progress updates.
26
19
  *
@@ -85,7 +78,7 @@ function createProgressCardMarkdownSections(progressCard: NonNullable<ChatMessag
85
78
  }
86
79
 
87
80
  if (normalizedNow) {
88
- markdownSections.push(`**${PROGRESS_NOW_LABEL}:** ${normalizedNow}`);
81
+ markdownSections.push(normalizedNow);
89
82
  }
90
83
 
91
84
  const itemLines: Array<string> = [];
@@ -48,6 +48,13 @@ const DEPRECATED_TOP_LEVEL_COMMAND_MESSAGES: Readonly<Record<string, string>> =
48
48
  'start-pipelines-server': OLD_PIPELINE_SYSTEM_DEPRECATION_MESSAGE,
49
49
  };
50
50
 
51
+ /**
52
+ * Raw top-level CLI arguments that print the Promptbook version.
53
+ *
54
+ * @private internal constant of `promptbookCli`
55
+ */
56
+ const VERSION_OPTION_ARGUMENTS: ReadonlySet<string> = new Set(['-v', '--version']);
57
+
51
58
  /**
52
59
  * Runs CLI utilities of Promptbook package
53
60
  *
@@ -65,8 +72,15 @@ export async function promptbookCli(): Promise<void> {
65
72
  );
66
73
  }
67
74
 
68
- const cliArguments = process.argv.slice(2);
69
- const isVerbose = cliArguments.some((arg) => arg === '--verbose' || arg === '-v');
75
+ const commandLineArguments = process.argv.slice(2);
76
+ const isVersionRequested = isTopLevelVersionRequested(commandLineArguments);
77
+
78
+ if (isVersionRequested) {
79
+ console.info(PROMPTBOOK_ENGINE_VERSION);
80
+ return process.exit(0);
81
+ }
82
+
83
+ const isVerbose = commandLineArguments.some((argument) => argument === '--verbose' || argument === '-v');
70
84
  // <- TODO: Can be this be done with commander before the commander commands are initialized?
71
85
  if (isVerbose) {
72
86
  console.info(
@@ -104,7 +118,7 @@ export async function promptbookCli(): Promise<void> {
104
118
  // TODO: [🧠] Should it be here or not> $addGlobalOptionsToCommand(program);
105
119
  program.commands.forEach($addGlobalOptionsToCommand);
106
120
 
107
- if (cliArguments.length === 0) {
121
+ if (commandLineArguments.length === 0) {
108
122
  program.outputHelp();
109
123
  return process.exit(0);
110
124
  }
@@ -112,6 +126,17 @@ export async function promptbookCli(): Promise<void> {
112
126
  program.parse(process.argv);
113
127
  }
114
128
 
129
+ /**
130
+ * Checks whether the invocation asks for the root Promptbook CLI version.
131
+ *
132
+ * @private internal utility of `promptbookCli`
133
+ */
134
+ function isTopLevelVersionRequested(commandLineArguments: ReadonlyArray<string>): boolean {
135
+ const firstCommandLineArgument = commandLineArguments[0];
136
+
137
+ return firstCommandLineArgument !== undefined && VERSION_OPTION_ARGUMENTS.has(firstCommandLineArgument);
138
+ }
139
+
115
140
  /**
116
141
  * Adds one deprecation notice to each configured top-level legacy command.
117
142
  */