@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
@@ -36,7 +36,14 @@ const nextConfig: NextConfig = {
36
36
  eslint: {
37
37
  ignoreDuringBuilds: isNextValidationIgnored,
38
38
  },
39
- serverExternalPackages: ['pg', '@napi-rs/canvas', 'playwright', 'playwright-core'],
39
+ serverExternalPackages: [
40
+ 'pg',
41
+ 'better-sqlite3',
42
+ '@prisma/studio-core',
43
+ '@napi-rs/canvas',
44
+ 'playwright',
45
+ 'playwright-core',
46
+ ],
40
47
  typescript: {
41
48
  ignoreBuildErrors: isNextValidationIgnored,
42
49
  },
@@ -28,6 +28,8 @@ const APP_E2E_ENV = {
28
28
  ADMIN_PASSWORD: 'e2e-admin-password',
29
29
  NEXT_DIST_DIR: '.next-e2e',
30
30
  NEXT_PUBLIC_SITE_URL: APP_URL,
31
+ PTBK_AGENTS_SERVER_DATABASE: 'supabase',
32
+ PTBK_AGENTS_SERVER_SQLITE_PATH: '',
31
33
  SUPABASE_TABLE_PREFIX: '',
32
34
  POSTGRES_URL: '',
33
35
  DATABASE_URL: '',
@@ -6,7 +6,7 @@ import Link from 'next/link';
6
6
  *
7
7
  * @private admin configuration UI helper
8
8
  */
9
- type AdminConfigurationPage = 'metadata' | 'limits';
9
+ type AdminConfigurationPage = 'environment' | 'metadata' | 'limits';
10
10
 
11
11
  /**
12
12
  * One shared navigation item rendered in the configuration shell.
@@ -15,15 +15,21 @@ type AdminConfigurationPage = 'metadata' | 'limits';
15
15
  */
16
16
  const ADMIN_CONFIGURATION_NAVIGATION_ITEMS: ReadonlyArray<{
17
17
  readonly id: AdminConfigurationPage;
18
- readonly href: '/admin/metadata' | '/admin/limits';
18
+ readonly href: '/admin/environment' | '/admin/metadata' | '/admin/limits';
19
19
  readonly label: string;
20
20
  readonly description: string;
21
21
  }> = [
22
+ {
23
+ id: 'environment',
24
+ href: '/admin/environment',
25
+ label: 'Environment variables',
26
+ description: 'VPS-wide .env values with secrets masked in the browser.',
27
+ },
22
28
  {
23
29
  id: 'metadata',
24
30
  href: '/admin/metadata',
25
31
  label: 'Metadata',
26
- description: 'Feature flags, text settings, and legacy compatibility keys.',
32
+ description: 'Domain-specific feature flags, text settings, and compatibility keys.',
27
33
  },
28
34
  {
29
35
  id: 'limits',
@@ -44,7 +50,7 @@ type AdminConfigurationShellProps = {
44
50
  };
45
51
 
46
52
  /**
47
- * Shared page shell used by the Metadata and Limits admin pages.
53
+ * Shared page shell used by the Environment variables, Metadata, and Limits admin pages.
48
54
  */
49
55
  export function AdminConfigurationShell({ activePage, children }: AdminConfigurationShellProps) {
50
56
  return (
@@ -53,11 +59,11 @@ export function AdminConfigurationShell({ activePage, children }: AdminConfigura
53
59
  <div>
54
60
  <h1 className="text-3xl font-light text-gray-900">Server configuration</h1>
55
61
  <p className="mt-1 max-w-3xl text-sm text-gray-500">
56
- Manage raw metadata together with dedicated operational limits. Deprecated compatibility metadata
57
- stays visible, but active quota changes belong on the Limits page.
62
+ Manage VPS-wide environment variables, domain-specific metadata, and dedicated operational
63
+ limits from one configuration area.
58
64
  </p>
59
65
  </div>
60
- <div className="grid gap-3 md:grid-cols-2">
66
+ <div className="grid gap-3 md:grid-cols-3">
61
67
  {ADMIN_CONFIGURATION_NAVIGATION_ITEMS.map((item) => {
62
68
  const isActive = item.id === activePage;
63
69
 
@@ -0,0 +1,225 @@
1
+ 'use client';
2
+
3
+ import { Loader2, Save, ServerCog } from 'lucide-react';
4
+ import { useEffect, useState } from 'react';
5
+ import { Card } from '../../../components/Homepage/Card';
6
+
7
+ /**
8
+ * Code-runner API response.
9
+ */
10
+ type CodeRunnersResponse = {
11
+ readonly agent?: string;
12
+ readonly model?: string;
13
+ readonly thinkingLevel?: string;
14
+ readonly status?: string;
15
+ readonly applyResult?: {
16
+ readonly isAvailable: boolean;
17
+ readonly output: string;
18
+ } | null;
19
+ readonly error?: string;
20
+ };
21
+
22
+ /**
23
+ * Supported runner options shown by the standalone UI.
24
+ */
25
+ const RUNNER_OPTIONS = [
26
+ { value: 'github-copilot', label: 'GitHub Copilot' },
27
+ { value: 'openai-codex', label: 'OpenAI Codex' },
28
+ { value: 'claude-code', label: 'Claude Code' },
29
+ { value: 'opencode', label: 'Opencode' },
30
+ { value: 'gemini', label: 'Gemini' },
31
+ ] as const;
32
+
33
+ /**
34
+ * Shared input styling for code-runner controls.
35
+ */
36
+ const INPUT_CLASS_NAME =
37
+ 'w-full rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 disabled:bg-gray-50 disabled:text-gray-500';
38
+
39
+ /**
40
+ * Client UI for configuring the local coding runner used by durable chats.
41
+ */
42
+ export function CodeRunnersClient() {
43
+ const [agent, setAgent] = useState('github-copilot');
44
+ const [model, setModel] = useState('gpt-5.4');
45
+ const [thinkingLevel, setThinkingLevel] = useState('xhigh');
46
+ const [status, setStatus] = useState('');
47
+ const [isLoading, setIsLoading] = useState(true);
48
+ const [isSaving, setIsSaving] = useState(false);
49
+ const [errorMessage, setErrorMessage] = useState<string | null>(null);
50
+ const [successMessage, setSuccessMessage] = useState<string | null>(null);
51
+ const [applyOutput, setApplyOutput] = useState<string | null>(null);
52
+
53
+ useEffect(() => {
54
+ void loadConfiguration();
55
+ }, []);
56
+
57
+ /**
58
+ * Loads current code-runner settings.
59
+ */
60
+ async function loadConfiguration(): Promise<void> {
61
+ try {
62
+ setIsLoading(true);
63
+ setErrorMessage(null);
64
+ const response = await fetch('/api/admin/code-runners', { cache: 'no-store' });
65
+ const payload = (await response.json()) as CodeRunnersResponse;
66
+
67
+ if (!response.ok) {
68
+ throw new Error(payload.error || 'Failed to load code-runner configuration.');
69
+ }
70
+
71
+ setAgent(payload.agent || 'github-copilot');
72
+ setModel(payload.model || 'gpt-5.4');
73
+ setThinkingLevel(payload.thinkingLevel || 'xhigh');
74
+ setStatus(payload.status || '');
75
+ } catch (error) {
76
+ setErrorMessage(error instanceof Error ? error.message : 'Failed to load code-runner configuration.');
77
+ } finally {
78
+ setIsLoading(false);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Saves code-runner settings into `.env`.
84
+ */
85
+ async function saveConfiguration(applyRuntimeConfiguration: boolean): Promise<void> {
86
+ try {
87
+ setIsSaving(true);
88
+ setErrorMessage(null);
89
+ setSuccessMessage(null);
90
+ setApplyOutput(null);
91
+
92
+ const response = await fetch('/api/admin/code-runners', {
93
+ method: 'PATCH',
94
+ headers: {
95
+ 'Content-Type': 'application/json',
96
+ },
97
+ body: JSON.stringify({ agent, model, thinkingLevel, applyRuntimeConfiguration }),
98
+ });
99
+ const payload = (await response.json()) as CodeRunnersResponse;
100
+
101
+ if (!response.ok) {
102
+ throw new Error(payload.error || 'Failed to save code-runner configuration.');
103
+ }
104
+
105
+ setAgent(payload.agent || agent);
106
+ setModel(payload.model || model);
107
+ setThinkingLevel(payload.thinkingLevel || thinkingLevel);
108
+ setStatus(payload.status || '');
109
+ setApplyOutput(payload.applyResult?.output || null);
110
+ setSuccessMessage(
111
+ applyRuntimeConfiguration
112
+ ? 'Code-runner configuration was saved and applied.'
113
+ : 'Code-runner configuration was saved.',
114
+ );
115
+ } catch (error) {
116
+ setErrorMessage(error instanceof Error ? error.message : 'Failed to save code-runner configuration.');
117
+ } finally {
118
+ setIsSaving(false);
119
+ }
120
+ }
121
+
122
+ return (
123
+ <div className="container mx-auto space-y-6 px-4 py-8">
124
+ <div className="mt-20">
125
+ <h1 className="text-3xl font-light text-gray-900">Code runners</h1>
126
+ <p className="mt-1 text-sm text-gray-500">
127
+ Configure the local runner used by the standalone Agents Server durable chat worker.
128
+ </p>
129
+ </div>
130
+
131
+ {errorMessage && (
132
+ <div className="rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
133
+ {errorMessage}
134
+ </div>
135
+ )}
136
+ {successMessage && (
137
+ <div className="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-700">
138
+ {successMessage}
139
+ </div>
140
+ )}
141
+
142
+ <Card className="hover:border-gray-200 hover:shadow-md">
143
+ <div className="grid gap-5 md:grid-cols-3">
144
+ <label className="space-y-2">
145
+ <span className="text-sm font-semibold text-slate-700">Runner</span>
146
+ <select
147
+ value={agent}
148
+ onChange={(event) => setAgent(event.target.value)}
149
+ disabled={isLoading || isSaving}
150
+ className={INPUT_CLASS_NAME}
151
+ >
152
+ {RUNNER_OPTIONS.map((option) => (
153
+ <option key={option.value} value={option.value}>
154
+ {option.label}
155
+ </option>
156
+ ))}
157
+ </select>
158
+ </label>
159
+ <label className="space-y-2">
160
+ <span className="text-sm font-semibold text-slate-700">Model</span>
161
+ <input
162
+ type="text"
163
+ value={model}
164
+ onChange={(event) => setModel(event.target.value)}
165
+ disabled={isLoading || isSaving}
166
+ className={INPUT_CLASS_NAME}
167
+ />
168
+ </label>
169
+ <label className="space-y-2">
170
+ <span className="text-sm font-semibold text-slate-700">Thinking level</span>
171
+ <input
172
+ type="text"
173
+ value={thinkingLevel}
174
+ onChange={(event) => setThinkingLevel(event.target.value)}
175
+ disabled={isLoading || isSaving}
176
+ className={INPUT_CLASS_NAME}
177
+ />
178
+ </label>
179
+ </div>
180
+
181
+ <div className="mt-6 flex flex-wrap items-center gap-3">
182
+ <button
183
+ type="button"
184
+ onClick={() => void saveConfiguration(false)}
185
+ disabled={isLoading || isSaving}
186
+ className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
187
+ >
188
+ {isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
189
+ Save runner
190
+ </button>
191
+ <button
192
+ type="button"
193
+ onClick={() => void saveConfiguration(true)}
194
+ disabled={isLoading || isSaving}
195
+ className="inline-flex items-center gap-2 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-60"
196
+ >
197
+ {isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <ServerCog className="h-4 w-4" />}
198
+ Save and apply runner
199
+ </button>
200
+ </div>
201
+ </Card>
202
+
203
+ {applyOutput ? (
204
+ <pre className="max-h-72 overflow-auto rounded-xl border border-slate-200 bg-slate-950 p-4 text-xs text-slate-100">
205
+ {applyOutput}
206
+ </pre>
207
+ ) : null}
208
+
209
+ <Card className="hover:border-gray-200 hover:shadow-md">
210
+ <div className="space-y-3">
211
+ <h2 className="text-lg font-semibold text-slate-900">Authentication</h2>
212
+ <p className="text-sm text-slate-600">
213
+ GitHub Copilot still requires an interactive CLI login and project trust setup on the VPS
214
+ terminal. Use <span className="font-mono">sudo -u $USER copilot</span>, run{' '}
215
+ <span className="font-mono">/login</span> when prompted, trust the install directory, then
216
+ restart the pm2 process.
217
+ </p>
218
+ <pre className="max-h-64 overflow-auto rounded-xl border border-slate-200 bg-slate-950 p-4 text-xs text-slate-100">
219
+ {status || 'Runner status was not available.'}
220
+ </pre>
221
+ </div>
222
+ </Card>
223
+ </div>
224
+ );
225
+ }
@@ -0,0 +1,14 @@
1
+ import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
2
+ import { isUserGlobalAdmin } from '../../../utils/isUserGlobalAdmin';
3
+ import { CodeRunnersClient } from './CodeRunnersClient';
4
+
5
+ /**
6
+ * Super-admin page for configuring standalone code runners.
7
+ */
8
+ export default async function CodeRunnersPage() {
9
+ if (!(await isUserGlobalAdmin())) {
10
+ return <ForbiddenPage />;
11
+ }
12
+
13
+ return <CodeRunnersClient />;
14
+ }
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+ import type { AgentsServerDatabaseMode } from '../../../database/agentsServerDatabaseMode';
5
+
6
+ /**
7
+ * Props consumed by the lazy Embedded Prisma Studio client.
8
+ *
9
+ * @private route component props of DatabaseAdminPage
10
+ */
11
+ type DatabaseAdminClientProps = {
12
+ readonly databaseMode: AgentsServerDatabaseMode;
13
+ };
14
+
15
+ /**
16
+ * Client-only Embedded Prisma Studio surface.
17
+ */
18
+ const DatabaseAdminStudioSurface = dynamic(
19
+ () => import('./DatabaseAdminStudioSurface').then((module) => module.DatabaseAdminStudioSurface),
20
+ {
21
+ ssr: false,
22
+ loading: () => (
23
+ <div className="flex h-full items-center justify-center text-sm text-gray-500">Loading database...</div>
24
+ ),
25
+ },
26
+ );
27
+
28
+ /**
29
+ * Renders the Embedded Prisma Studio client after hydration.
30
+ *
31
+ * @param props - Active database mode.
32
+ * @returns Database admin client.
33
+ *
34
+ * @private route component of DatabaseAdminPage
35
+ */
36
+ export function DatabaseAdminClient({ databaseMode }: DatabaseAdminClientProps) {
37
+ return <DatabaseAdminStudioSurface databaseMode={databaseMode} />;
38
+ }
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { createStudioBFFClient } from '@prisma/studio-core/data/bff';
4
+ import { createPostgresAdapter } from '@prisma/studio-core/data/postgres-core';
5
+ import { createSQLiteAdapter } from '@prisma/studio-core/data/sqlite-core';
6
+ import { Studio } from '@prisma/studio-core/ui';
7
+ import { useMemo } from 'react';
8
+ import type { AgentsServerDatabaseMode } from '../../../database/agentsServerDatabaseMode';
9
+
10
+ /**
11
+ * Backend endpoint used by Embedded Prisma Studio.
12
+ */
13
+ const DATABASE_ADMIN_STUDIO_ENDPOINT = '/api/admin/database/studio';
14
+
15
+ /**
16
+ * Props consumed by the hydrated Embedded Prisma Studio surface.
17
+ *
18
+ * @private route component props of DatabaseAdminPage
19
+ */
20
+ type DatabaseAdminStudioSurfaceProps = {
21
+ readonly databaseMode: AgentsServerDatabaseMode;
22
+ };
23
+
24
+ /**
25
+ * Renders Prisma Studio with an adapter matching the configured database backend.
26
+ *
27
+ * @param props - Active database mode.
28
+ * @returns Embedded Prisma Studio surface.
29
+ *
30
+ * @private route component of DatabaseAdminPage
31
+ */
32
+ export function DatabaseAdminStudioSurface({ databaseMode }: DatabaseAdminStudioSurfaceProps) {
33
+ const adapter = useMemo(() => {
34
+ const executor = createStudioBFFClient({
35
+ url: DATABASE_ADMIN_STUDIO_ENDPOINT,
36
+ });
37
+
38
+ return databaseMode === 'sqlite' ? createSQLiteAdapter({ executor }) : createPostgresAdapter({ executor });
39
+ }, [databaseMode]);
40
+
41
+ return <Studio adapter={adapter} />;
42
+ }
@@ -0,0 +1,33 @@
1
+ import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
2
+ import { resolveAgentsServerDatabaseMode } from '../../../database/agentsServerDatabaseMode';
3
+ import { isUserGlobalAdmin } from '../../../utils/isUserGlobalAdmin';
4
+ import { DatabaseAdminClient } from './DatabaseAdminClient';
5
+
6
+ /**
7
+ * Super-admin page exposing raw database access through Embedded Prisma Studio.
8
+ */
9
+ export default async function DatabaseAdminPage() {
10
+ if (!(await isUserGlobalAdmin())) {
11
+ return <ForbiddenPage />;
12
+ }
13
+
14
+ const databaseMode = resolveAgentsServerDatabaseMode();
15
+ const databaseModeLabel = databaseMode === 'sqlite' ? 'SQLite' : 'Supabase';
16
+
17
+ return (
18
+ <div className="flex h-[calc(100vh-60px)] flex-col overflow-hidden bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-100">
19
+ <div className="flex flex-wrap items-center justify-between gap-3 border-b border-gray-200 px-5 py-3 dark:border-gray-800">
20
+ <div>
21
+ <p className="text-xs uppercase tracking-[0.25em] text-gray-400">Super Admin</p>
22
+ <h1 className="text-xl font-semibold">Database</h1>
23
+ </div>
24
+ <span className="rounded border border-gray-200 px-2 py-1 text-xs font-medium text-gray-600 dark:border-gray-700 dark:text-gray-300">
25
+ {databaseModeLabel}
26
+ </span>
27
+ </div>
28
+ <div className="min-h-0 flex-1">
29
+ <DatabaseAdminClient databaseMode={databaseMode} />
30
+ </div>
31
+ </div>
32
+ );
33
+ }