@promptbook/cli 0.113.0-5 → 0.113.0-8

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 (123) hide show
  1. package/apps/agents-server/src/app/admin/update/AdvancedOriginRepositoryPanel.tsx +76 -0
  2. package/apps/agents-server/src/app/admin/update/CurrentDeploymentCard.tsx +160 -0
  3. package/apps/agents-server/src/app/admin/update/PendingCommitsCard.tsx +86 -0
  4. package/apps/agents-server/src/app/admin/update/TargetEnvironmentCard.tsx +331 -0
  5. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +20 -873
  6. package/apps/agents-server/src/app/admin/update/UpdateJobCard.tsx +329 -0
  7. package/apps/agents-server/src/app/admin/update/UpdateOverview.ts +74 -0
  8. package/apps/agents-server/src/app/admin/update/buildDeploymentTimeBehindLabel.ts +29 -0
  9. package/apps/agents-server/src/app/admin/update/formatHumanReadableTimestamp.ts +18 -0
  10. package/apps/agents-server/src/app/admin/update/getUpdateJobFailureMessage.ts +13 -0
  11. package/apps/agents-server/src/app/admin/update/getUpdateJobSuccessMessage.ts +13 -0
  12. package/apps/agents-server/src/app/admin/update/useUpdateClientState.ts +441 -0
  13. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +13 -1
  14. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageClient.tsx +14 -559
  15. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageHeader.tsx +84 -0
  16. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/AgentCodePageSection.tsx +52 -0
  17. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedCodePreview.tsx +138 -0
  18. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/GeneratedHarnessSection.tsx +98 -0
  19. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/SourceBookSection.tsx +66 -0
  20. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeErrorBanner.tsx +30 -0
  21. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/TranspiledCodeExportWarningBanner.tsx +62 -0
  22. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/Transpiler.ts +9 -0
  23. package/apps/agents-server/src/app/agents/[agentName]/export-as-transpiled-code/useAgentCodeExportState.ts +478 -0
  24. package/apps/agents-server/src/app/system/utilities/mocked-chats/cloneMockedChatPreset.ts +17 -0
  25. package/apps/agents-server/src/app/system/utilities/mocked-chats/confirmDeleteMockedChat.ts +16 -0
  26. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedMessage.ts +81 -0
  27. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedParticipant.ts +146 -0
  28. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDraftWithUpdatedSettings.ts +19 -0
  29. package/apps/agents-server/src/app/system/utilities/mocked-chats/createDuplicatedDraft.ts +66 -0
  30. package/apps/agents-server/src/app/system/utilities/mocked-chats/findMockedChatById.ts +51 -0
  31. package/apps/agents-server/src/app/system/utilities/mocked-chats/persistMockedChats.ts +51 -0
  32. package/apps/agents-server/src/app/system/utilities/mocked-chats/useMockedChatsEditorState.ts +16 -426
  33. package/apps/agents-server/src/components/Homepage/buildFreeGraphBoxLayout.ts +446 -0
  34. package/apps/agents-server/src/components/Homepage/buildGraphLayoutNodes.ts +415 -157
  35. package/apps/agents-server/src/components/Homepage/useAgentsGraphCanvasState.ts +2 -1
  36. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +3 -1541
  37. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteQueryBuilder.ts +623 -0
  38. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/LocalSqliteSupabaseClient.ts +64 -0
  39. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/ensureTable.ts +93 -0
  40. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteFilters.ts +202 -0
  41. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteMutationPayload.ts +131 -0
  42. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteRowOperations.ts +102 -0
  43. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteSql.ts +59 -0
  44. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTableSchema.ts +329 -0
  45. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteTypes.ts +84 -0
  46. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/localSqliteValueCodec.ts +63 -0
  47. package/apps/agents-server/src/database/sqlite/localSqliteSupabase/normalizeSqliteError.ts +22 -0
  48. package/apps/agents-server/src/search/createDefaultServerSearchProviders/loadLocalOrganizationSearchDataset.ts +12 -1
  49. package/apps/agents-server/src/tools/send_email.ts +1 -2
  50. package/apps/agents-server/src/utils/agentOrganization/loadAgentOrganizationState.ts +13 -1
  51. package/apps/agents-server/src/utils/backup/createServerBackupZipStream.ts +7 -1382
  52. package/apps/agents-server/src/utils/backup/serverBackup/appendAgentBackupEntriesToZip.ts +27 -0
  53. package/apps/agents-server/src/utils/backup/serverBackup/appendConversationBackupEntriesToZip.ts +140 -0
  54. package/apps/agents-server/src/utils/backup/serverBackup/appendFileBackupEntriesToZip.ts +147 -0
  55. package/apps/agents-server/src/utils/backup/serverBackup/appendMessageBackupEntriesToZip.ts +65 -0
  56. package/apps/agents-server/src/utils/backup/serverBackup/appendMetadataBackupEntriesToZip.ts +39 -0
  57. package/apps/agents-server/src/utils/backup/serverBackup/appendSectionEntriesToZip.ts +92 -0
  58. package/apps/agents-server/src/utils/backup/serverBackup/appendUserBackupEntriesToZip.ts +80 -0
  59. package/apps/agents-server/src/utils/backup/serverBackup/createRedactedWalletBackupRecord.ts +34 -0
  60. package/apps/agents-server/src/utils/backup/serverBackup/createServerBackupManifest.ts +82 -0
  61. package/apps/agents-server/src/utils/backup/serverBackup/downloadBackupBinaryContent.ts +39 -0
  62. package/apps/agents-server/src/utils/backup/serverBackup/resolveFeedbackThreadMessages.ts +53 -0
  63. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupAttachments.ts +151 -0
  64. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupContext.ts +167 -0
  65. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupFilenames.ts +161 -0
  66. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupPreviews.ts +97 -0
  67. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupRowUtilities.ts +158 -0
  68. package/apps/agents-server/src/utils/backup/serverBackup/serverBackupTypes.ts +59 -0
  69. package/apps/agents-server/src/utils/createLocalAgentSourceImporter.ts +159 -0
  70. package/apps/agents-server/src/utils/createMissingImportedAgentFallback.ts +60 -0
  71. package/apps/agents-server/src/utils/customDomainRouting.ts +157 -12
  72. package/apps/agents-server/src/utils/externalChatRunner/processExternalUserChatJob.ts +1 -1
  73. package/apps/agents-server/src/utils/importAgentWithFallback.ts +1 -58
  74. package/apps/agents-server/src/utils/localAgentRouteReferences.ts +167 -0
  75. package/apps/agents-server/src/utils/localChatRunner/processLocalUserChatJob.ts +1 -1
  76. package/apps/agents-server/src/utils/managementApi/managementApiAgents.ts +17 -3
  77. package/apps/agents-server/src/utils/messages/sendMessage.ts +5 -3
  78. package/apps/agents-server/src/utils/resolveAgentStateFromSource.ts +7 -1
  79. package/apps/agents-server/src/utils/resolveInheritedAgentSource.ts +54 -5
  80. package/apps/agents-server/src/utils/resolveServerAgentContext.ts +12 -1
  81. package/apps/agents-server/src/utils/resolveStoredAgentState.ts +2 -1
  82. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition/SpeechToTextFailoverRecognitionProviderRuntime.ts +0 -1
  83. package/apps/agents-server/src/utils/speech-to-text/SpeechToTextFailoverRecognition.ts +34 -2
  84. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +39 -9
  85. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +5 -0
  86. package/esm/index.es.js +96 -57
  87. package/esm/index.es.js.map +1 -1
  88. package/esm/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  89. package/esm/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  90. package/esm/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  91. package/esm/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  92. package/esm/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  93. package/esm/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  94. package/esm/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  95. package/esm/src/version.d.ts +1 -1
  96. package/package.json +1 -1
  97. package/src/avatars/avatarAnimationScheduler.ts +2 -1
  98. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +6 -2
  99. package/src/avatars/visuals/octopus3d4AvatarVisual.ts +14 -18
  100. package/src/book-components/Chat/Chat/insertDictationChunk.ts +3 -3
  101. package/src/book-components/Chat/Chat/learnDictationDictionary.ts +1 -1
  102. package/src/book-components/Chat/Chat/useChatInputAreaDictation.ts +133 -26
  103. package/src/cli/cli-commands/agents-server/buildAgentsServer.ts +3 -1
  104. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +14 -21
  105. package/src/cli/cli-commands/coder/find-refactor-candidates.ts +9 -2
  106. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +1 -4
  107. package/src/cli/cli-commands/coder/run.ts +3 -27
  108. package/src/cli/cli-commands/common/createPositiveIntegerOptionParser.ts +31 -0
  109. package/src/commitments/TEAM/TEAM.ts +5 -5
  110. package/src/other/templates/getTemplatesPipelineCollection.ts +727 -781
  111. package/src/utils/ascii-art/convertImageDataToAsciiArt.ts +1 -4
  112. package/src/version.ts +2 -2
  113. package/src/versions.txt +1 -0
  114. package/umd/index.umd.js +96 -57
  115. package/umd/index.umd.js.map +1 -1
  116. package/umd/scripts/find-refactor-candidates/find-refactor-candidates.d.ts +7 -0
  117. package/umd/scripts/find-refactor-candidates/selectMostImportantRefactorCandidates.d.ts +12 -0
  118. package/umd/src/book-components/Chat/Chat/insertDictationChunk.d.ts +1 -1
  119. package/umd/src/book-components/Chat/Chat/insertDictationChunk.test.d.ts +1 -0
  120. package/umd/src/book-components/Chat/Chat/learnDictationDictionary.test.d.ts +1 -0
  121. package/umd/src/book-components/Chat/Chat/refineFinalDictationChunk.test.d.ts +1 -0
  122. package/umd/src/cli/cli-commands/common/createPositiveIntegerOptionParser.d.ts +10 -0
  123. package/umd/src/version.d.ts +1 -1
@@ -1,72 +1,11 @@
1
1
  'use client';
2
2
 
3
- import { BookEditor } from '@promptbook-local/components';
4
- import type { AgentBasicInformation, string_book, string_url } from '@promptbook-local/types';
5
- import { AlertTriangleIcon, ChevronDownIcon, CodeIcon, DownloadIcon, PencilIcon } from 'lucide-react';
6
- import Link from 'next/link';
7
- import { type ReactNode, useCallback, useEffect, useState } from 'react';
8
- import { MonacoEditorWithShadowDom } from '../../../../components/_utils/MonacoEditorWithShadowDom';
9
- import { usePromptbookTheme } from '../../../../components/ThemeMode/usePromptbookTheme';
10
- import { downloadBlob, parseFilenameFromContentDisposition } from '../../../../utils/download/browserFileDownload';
11
- import { getTranspiledCodeFileMetadata } from '../../../../utils/transpilers/getTranspiledCodeFileMetadata';
3
+ import type { string_book, string_url } from '@promptbook-local/types';
12
4
  import type { TranspiledAgentExportWarning } from '../../../../utils/transpilers/getTranspiledAgentExportWarnings';
13
- import { resolveAgentAvatarImageUrl } from '../../../../../../../src/utils/agents/resolveAgentAvatarImageUrl';
14
-
15
- /**
16
- * Type describing one selectable transpiler.
17
- */
18
- type Transpiler = {
19
- readonly name: string;
20
- readonly title: string;
21
- };
22
-
23
- /**
24
- * Minimal payload returned by the transpiler-list endpoint.
25
- */
26
- type TranspilerListResponse = {
27
- readonly transpilers?: Array<Transpiler>;
28
- };
29
-
30
- /**
31
- * Result returned by the transpilation endpoint.
32
- */
33
- type TranspilationResult = {
34
- readonly code: string;
35
- readonly transpiler: Transpiler;
36
- };
37
-
38
- /**
39
- * Minimal API error payload accepted from export endpoints.
40
- */
41
- type TranspiledCodeApiErrorPayload = {
42
- readonly error?: string;
43
- readonly message?: string;
44
- };
45
-
46
- /**
47
- * Props for one page section card.
48
- */
49
- type AgentCodePageSectionProps = {
50
- /**
51
- * Section heading shown above the content.
52
- */
53
- readonly title: string;
54
-
55
- /**
56
- * Supporting copy shown under the title.
57
- */
58
- readonly description: string;
59
-
60
- /**
61
- * Optional header actions rendered beside the title.
62
- */
63
- readonly actions?: ReactNode;
64
-
65
- /**
66
- * Main section content.
67
- */
68
- readonly children: ReactNode;
69
- };
5
+ import { AgentCodePageHeader } from './AgentCodePageHeader';
6
+ import { GeneratedHarnessSection } from './GeneratedHarnessSection';
7
+ import { SourceBookSection } from './SourceBookSection';
8
+ import { useAgentCodeExportState } from './useAgentCodeExportState';
70
9
 
71
10
  /**
72
11
  * Props for the export-as-transpiled-code page client.
@@ -95,509 +34,25 @@ type AgentCodePageClientProps = {
95
34
  readonly exportWarnings: ReadonlyArray<TranspiledAgentExportWarning>;
96
35
  };
97
36
 
98
- /**
99
- * Creates the JSON export API path for one agent.
100
- *
101
- * @param agentName - Routed agent name.
102
- * @returns Relative API path used for transpiler listing and code generation.
103
- */
104
- function createTranspiledCodeApiPath(agentName: string): string {
105
- return `/agents/${encodeURIComponent(agentName)}/export-as-transpiled-code/api`;
106
- }
107
-
108
- /**
109
- * Creates the ZIP download API path for one agent and selected transpiler.
110
- *
111
- * @param agentName - Routed agent name.
112
- * @param transpilerName - Selected transpiler identifier.
113
- * @returns Relative download URL with the selected transpiler encoded in the query string.
114
- */
115
- function createTranspiledCodeDownloadApiPath(agentName: string, transpilerName: string): string {
116
- const searchParams = new URLSearchParams({ transpilerName });
117
- return `${createTranspiledCodeApiPath(agentName)}/download?${searchParams.toString()}`;
118
- }
119
-
120
- /**
121
- * Reads a user-facing API error from a failed export response.
122
- *
123
- * @param response - Failed HTTP response.
124
- * @param fallbackMessage - Fallback message when the body has no structured error.
125
- * @returns Friendly error message suitable for rendering in the UI.
126
- */
127
- async function resolveTranspiledCodeApiErrorMessage(response: Response, fallbackMessage: string): Promise<string> {
128
- try {
129
- const payload = (await response.json()) as TranspiledCodeApiErrorPayload;
130
- const message = payload.message || payload.error;
131
-
132
- if (message && message.trim().length > 0) {
133
- return message.trim();
134
- }
135
- } catch {
136
- // Keep the fallback message when the error body is not valid JSON.
137
- }
138
-
139
- return fallbackMessage;
140
- }
141
-
142
- /**
143
- * Resolves the avatar image URL for the current agent header.
144
- *
145
- * @param options - Agent identity and loaded profile data.
146
- * @returns Avatar URL preferred by the profile, falling back to the default generated avatar.
147
- */
148
- function resolveAgentAvatarSource(options: {
149
- readonly agentName: string;
150
- readonly agentProfile: AgentBasicInformation | null;
151
- readonly publicUrl: string_url;
152
- }): string {
153
- const { agentName, agentProfile, publicUrl } = options;
154
- const fallbackIdentifier = agentProfile?.permanentId || agentName;
155
-
156
- if (!agentProfile) {
157
- return `/agents/${encodeURIComponent(fallbackIdentifier)}/images/default-avatar.png`;
158
- }
159
-
160
- return (
161
- resolveAgentAvatarImageUrl({
162
- agent: agentProfile,
163
- baseUrl: publicUrl,
164
- }) || `/agents/${encodeURIComponent(fallbackIdentifier)}/images/default-avatar.png`
165
- );
166
- }
167
-
168
- /**
169
- * Shared card section used on the export page.
170
- */
171
- function AgentCodePageSection({ title, description, actions, children }: AgentCodePageSectionProps) {
172
- return (
173
- <section className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
174
- <div className="flex flex-col gap-4 border-b border-slate-200 px-5 py-4 sm:flex-row sm:items-start sm:justify-between dark:border-slate-800">
175
- <div className="min-w-0">
176
- <h2 className="text-lg font-semibold text-slate-900 dark:text-slate-100">{title}</h2>
177
- <p className="mt-1 text-sm text-slate-600 dark:text-slate-400">{description}</p>
178
- </div>
179
-
180
- {actions ? <div className="flex shrink-0 flex-wrap items-center gap-3">{actions}</div> : null}
181
- </div>
182
-
183
- <div className="p-5">{children}</div>
184
- </section>
185
- );
186
- }
187
-
188
- /**
189
- * Props for the export warning banner.
190
- */
191
- type TranspiledCodeExportWarningBannerProps = {
192
- /**
193
- * Non-transpilable commitment warnings computed for the current agent.
194
- */
195
- readonly warnings: ReadonlyArray<TranspiledAgentExportWarning>;
196
- };
197
-
198
- /**
199
- * Renders a warning banner when the current agent uses commitments that cannot be exported 1:1.
200
- */
201
- function TranspiledCodeExportWarningBanner({ warnings }: TranspiledCodeExportWarningBannerProps) {
202
- if (warnings.length === 0) {
203
- return null;
204
- }
205
-
206
- return (
207
- <div
208
- className="rounded-xl border border-amber-200 bg-amber-50 p-4 text-amber-950 shadow-sm dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-100"
209
- role="alert"
210
- >
211
- <div className="flex items-start gap-3">
212
- <AlertTriangleIcon className="mt-0.5 h-5 w-5 shrink-0 text-amber-600 dark:text-amber-300" />
213
- <div className="min-w-0">
214
- <p className="text-sm font-semibold">
215
- Some agent functionality cannot be transpiled exactly.
216
- </p>
217
- <p className="mt-1 text-sm text-amber-900/90 dark:text-amber-100/80">
218
- The exported code may not behave 1:1 with the live agent in Agents Server.
219
- </p>
220
-
221
- <ul className="mt-3 space-y-3">
222
- {warnings.map((warning) => (
223
- <li key={warning.commitmentName} className="flex gap-2">
224
- <span className="mt-2 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500" />
225
- <div className="min-w-0">
226
- <p className="text-sm font-medium">
227
- <code className="rounded bg-amber-100 px-1 py-0.5 font-mono text-[0.85em] text-amber-950 dark:bg-amber-900/60 dark:text-amber-100">
228
- {warning.commitmentName}
229
- </code>
230
- </p>
231
- <p className="mt-1 text-sm text-amber-900/90 dark:text-amber-100/80">
232
- {warning.description}
233
- </p>
234
- </div>
235
- </li>
236
- ))}
237
- </ul>
238
- </div>
239
- </div>
240
- </div>
241
- );
242
- }
243
-
244
37
  /**
245
38
  * Handles the export-as-transpiled-code page.
246
39
  */
247
- export function AgentCodePageClient({
248
- agentName,
249
- agentSource,
250
- publicUrl,
251
- exportWarnings,
252
- }: AgentCodePageClientProps) {
253
- const { promptbookTheme } = usePromptbookTheme();
254
- const [agentProfile, setAgentProfile] = useState<AgentBasicInformation | null>(null);
255
- const [transpilers, setTranspilers] = useState<Array<Transpiler>>([]);
256
- const [selectedTranspilerName, setSelectedTranspilerName] = useState('');
257
- const [transpiledCode, setTranspiledCode] = useState('');
258
- const [isPageLoading, setIsPageLoading] = useState(true);
259
- const [isTranspiling, setIsTranspiling] = useState(false);
260
- const [isDownloading, setIsDownloading] = useState(false);
261
- const [pageErrorMessage, setPageErrorMessage] = useState<string | null>(null);
262
- const [transpileErrorMessage, setTranspileErrorMessage] = useState<string | null>(null);
263
- const [downloadErrorMessage, setDownloadErrorMessage] = useState<string | null>(null);
264
-
265
- const selectedTranspiler = transpilers.find((transpiler) => transpiler.name === selectedTranspilerName) || null;
266
- const agentDisplayName = agentProfile?.meta.fullname || agentName;
267
- const agentAvatarSource = resolveAgentAvatarSource({ agentName, agentProfile, publicUrl });
268
-
269
- useEffect(() => {
270
- const abortController = new AbortController();
271
- let isDisposed = false;
272
-
273
- setIsPageLoading(true);
274
- setPageErrorMessage(null);
275
- setTranspileErrorMessage(null);
276
- setDownloadErrorMessage(null);
277
- setAgentProfile(null);
278
- setTranspiledCode('');
279
- setTranspilers([]);
280
- setSelectedTranspilerName('');
281
-
282
- void (async () => {
283
- try {
284
- const response = await fetch(createTranspiledCodeApiPath(agentName), {
285
- method: 'GET',
286
- signal: abortController.signal,
287
- });
288
-
289
- if (!response.ok) {
290
- throw new Error(await resolveTranspiledCodeApiErrorMessage(response, 'Failed to load transpilers'));
291
- }
292
-
293
- const payload = (await response.json()) as TranspilerListResponse;
294
- if (isDisposed) {
295
- return;
296
- }
297
-
298
- const nextTranspilers = payload.transpilers || [];
299
- setTranspilers(nextTranspilers);
300
- setSelectedTranspilerName(nextTranspilers[0]?.name || '');
301
- } catch (error) {
302
- if (abortController.signal.aborted || isDisposed) {
303
- return;
304
- }
305
-
306
- setPageErrorMessage(error instanceof Error ? error.message : 'Failed to load transpilers');
307
- } finally {
308
- if (!isDisposed) {
309
- setIsPageLoading(false);
310
- }
311
- }
312
- })();
313
-
314
- void (async () => {
315
- try {
316
- const response = await fetch(`/agents/${encodeURIComponent(agentName)}/api/profile`, {
317
- method: 'GET',
318
- signal: abortController.signal,
319
- });
320
-
321
- if (!response.ok) {
322
- return;
323
- }
324
-
325
- const profile = (await response.json()) as AgentBasicInformation;
326
- if (!isDisposed) {
327
- setAgentProfile(profile);
328
- }
329
- } catch (error) {
330
- if (!abortController.signal.aborted) {
331
- console.error('Error fetching agent profile:', error);
332
- }
333
- }
334
- })();
335
-
336
- return () => {
337
- isDisposed = true;
338
- abortController.abort();
339
- };
340
- }, [agentName]);
341
-
342
- useEffect(() => {
343
- if (!selectedTranspilerName) {
344
- setIsTranspiling(false);
345
- setTranspiledCode('');
346
- return;
347
- }
348
-
349
- const abortController = new AbortController();
350
- let isDisposed = false;
351
-
352
- setIsTranspiling(true);
353
- setTranspileErrorMessage(null);
354
- setDownloadErrorMessage(null);
355
- setTranspiledCode('');
356
-
357
- void (async () => {
358
- try {
359
- const response = await fetch(createTranspiledCodeApiPath(agentName), {
360
- method: 'POST',
361
- headers: {
362
- 'Content-Type': 'application/json',
363
- },
364
- body: JSON.stringify({ transpilerName: selectedTranspilerName }),
365
- signal: abortController.signal,
366
- });
367
-
368
- if (!response.ok) {
369
- throw new Error(await resolveTranspiledCodeApiErrorMessage(response, 'Failed to transpile code'));
370
- }
371
-
372
- const result = (await response.json()) as TranspilationResult;
373
- if (!isDisposed) {
374
- setTranspiledCode(result.code);
375
- }
376
- } catch (error) {
377
- if (abortController.signal.aborted || isDisposed) {
378
- return;
379
- }
380
-
381
- setTranspileErrorMessage(error instanceof Error ? error.message : 'Failed to transpile code');
382
- } finally {
383
- if (!isDisposed) {
384
- setIsTranspiling(false);
385
- }
386
- }
387
- })();
388
-
389
- return () => {
390
- isDisposed = true;
391
- abortController.abort();
392
- };
393
- }, [agentName, selectedTranspilerName]);
394
-
395
- /**
396
- * Downloads the selected transpiled harness as a ZIP archive.
397
- */
398
- const handleDownloadTranspiledCode = useCallback(async () => {
399
- if (!selectedTranspilerName || isDownloading) {
400
- return;
401
- }
402
-
403
- setIsDownloading(true);
404
- setDownloadErrorMessage(null);
405
-
406
- try {
407
- const response = await fetch(createTranspiledCodeDownloadApiPath(agentName, selectedTranspilerName), {
408
- method: 'GET',
409
- });
410
-
411
- if (!response.ok) {
412
- throw new Error(await resolveTranspiledCodeApiErrorMessage(response, 'Failed to download ZIP export'));
413
- }
414
-
415
- const filename =
416
- parseFilenameFromContentDisposition(response.headers.get('Content-Disposition')) ||
417
- 'promptbook-agent-export.zip';
418
- const exportBlob = await response.blob();
419
- downloadBlob(exportBlob, filename);
420
- } catch (error) {
421
- setDownloadErrorMessage(error instanceof Error ? error.message : 'Failed to download ZIP export');
422
- } finally {
423
- setIsDownloading(false);
424
- }
425
- }, [agentName, isDownloading, selectedTranspilerName]);
426
-
427
- const downloadButtonLabel = isDownloading ? 'Preparing ZIP...' : 'Download ZIP';
428
- const isDownloadButtonDisabled = !selectedTranspilerName || isPageLoading || isTranspiling || isDownloading;
40
+ export function AgentCodePageClient({ agentName, agentSource, publicUrl, exportWarnings }: AgentCodePageClientProps) {
41
+ const state = useAgentCodeExportState(agentName);
429
42
 
430
43
  return (
431
44
  <div className="min-h-screen bg-slate-50 px-6 py-8 md:px-12 dark:bg-slate-950">
432
45
  <div className="mx-auto flex w-full max-w-6xl flex-col gap-6">
433
46
  <div className="overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
434
- <div className="flex flex-col gap-4 border-b border-slate-200 px-6 py-6 sm:flex-row sm:items-center dark:border-slate-800">
435
- {/* eslint-disable-next-line @next/next/no-img-element */}
436
- <img
437
- src={agentAvatarSource}
438
- alt={agentDisplayName}
439
- className="agent-avatar-pixelated h-16 w-16 rounded-full border-2 border-slate-200 object-cover dark:border-slate-700"
440
- />
441
-
442
- <div className="min-w-0 flex-1">
443
- <h1 className="truncate text-2xl font-bold text-slate-900 dark:text-slate-100">
444
- {agentDisplayName}
445
- </h1>
446
- <p className="mt-1 flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
447
- <CodeIcon className="h-4 w-4" />
448
- Export as transpiled code / agent harness
449
- </p>
450
- </div>
451
- </div>
47
+ <AgentCodePageHeader
48
+ agentName={agentName}
49
+ agentProfile={state.agentProfile}
50
+ publicUrl={publicUrl}
51
+ />
452
52
 
453
53
  <div className="grid gap-6 p-6">
454
- <AgentCodePageSection
455
- title="Source Book"
456
- description="Review the stored Book source used to create this agent. Editing stays in the dedicated Book editor."
457
- actions={
458
- <Link
459
- href={`/agents/${encodeURIComponent(agentName)}/book`}
460
- className="inline-flex items-center justify-center gap-2 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition-colors hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200 dark:hover:bg-slate-800"
461
- >
462
- <PencilIcon className="h-4 w-4" />
463
- Edit Book
464
- </Link>
465
- }
466
- >
467
- <div className="h-[28rem] overflow-hidden rounded-xl border border-slate-200 dark:border-slate-700">
468
- <BookEditor
469
- className="h-full w-full"
470
- height={null}
471
- value={agentSource}
472
- isReadonly
473
- isUploadButtonShown={false}
474
- isCameraButtonShown={false}
475
- isDownloadButtonShown={false}
476
- isAboutButtonShown={false}
477
- isFullscreenButtonShown={false}
478
- translations={{ readonlyMessage: 'Use Edit Book to change the source.' }}
479
- theme={promptbookTheme}
480
- />
481
- </div>
482
- </AgentCodePageSection>
483
-
484
- <AgentCodePageSection
485
- title="Generated Harness"
486
- description="Generate runnable output from the current agent and download the source book plus transpiled harness as one ZIP archive."
487
- actions={
488
- <button
489
- type="button"
490
- onClick={() => void handleDownloadTranspiledCode()}
491
- disabled={isDownloadButtonDisabled}
492
- className="inline-flex items-center justify-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
493
- >
494
- <DownloadIcon className="h-4 w-4" />
495
- {downloadButtonLabel}
496
- </button>
497
- }
498
- >
499
- <div className="space-y-4">
500
- <TranspiledCodeExportWarningBanner warnings={exportWarnings} />
501
- <div>
502
- <label
503
- htmlFor="agent-code-transpiler"
504
- className="mb-2 block text-sm font-medium text-slate-700 dark:text-slate-300"
505
- >
506
- Select transpiler
507
- </label>
508
- <div className="relative">
509
- <select
510
- id="agent-code-transpiler"
511
- value={selectedTranspilerName}
512
- onChange={(event) => setSelectedTranspilerName(event.target.value)}
513
- disabled={isPageLoading || transpilers.length === 0}
514
- className="w-full appearance-none rounded-md border border-slate-300 bg-white px-3 py-2 pr-9 text-sm text-slate-900 shadow-sm outline-none transition focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 disabled:cursor-not-allowed disabled:opacity-60 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-100"
515
- >
516
- {transpilers.length === 0 ? (
517
- <option value="">
518
- {isPageLoading ? 'Loading transpilers...' : 'No transpilers available'}
519
- </option>
520
- ) : (
521
- transpilers.map((transpiler) => (
522
- <option key={transpiler.name} value={transpiler.name}>
523
- {transpiler.title}
524
- </option>
525
- ))
526
- )}
527
- </select>
528
- <ChevronDownIcon className="pointer-events-none absolute right-3 top-2.5 h-4 w-4 text-slate-400" />
529
- </div>
530
- </div>
531
-
532
- {pageErrorMessage ? (
533
- <div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300">
534
- {pageErrorMessage}
535
- </div>
536
- ) : null}
537
-
538
- {transpileErrorMessage ? (
539
- <div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300">
540
- {transpileErrorMessage}
541
- </div>
542
- ) : null}
543
-
544
- {downloadErrorMessage ? (
545
- <div className="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-300">
546
- {downloadErrorMessage}
547
- </div>
548
- ) : null}
549
-
550
- <div className="overflow-hidden rounded-xl border border-slate-200 bg-slate-50 dark:border-slate-700 dark:bg-slate-950">
551
- <div className="flex flex-wrap items-center justify-between gap-3 border-b border-slate-200 px-4 py-3 dark:border-slate-700">
552
- <h3 className="text-base font-semibold text-slate-900 dark:text-slate-100">
553
- {selectedTranspiler?.title || 'Generated code'}
554
- </h3>
555
- {(isTranspiling || isDownloading) && (
556
- <div className="text-sm text-slate-500 dark:text-slate-400" role="status" aria-live="polite">
557
- {isDownloading ? 'Preparing ZIP archive...' : 'Generating code...'}
558
- </div>
559
- )}
560
- </div>
561
-
562
- <div className="p-4">
563
- {transpiledCode ? (
564
- <div className="h-[32rem] overflow-hidden rounded-lg border border-slate-200 dark:border-slate-700">
565
- <MonacoEditorWithShadowDom
566
- value={transpiledCode}
567
- language={getTranspiledCodeFileMetadata(selectedTranspilerName).language}
568
- options={{
569
- readOnly: true,
570
- minimap: { enabled: false },
571
- fontSize: 14,
572
- lineNumbers: 'on',
573
- scrollBeyondLastLine: false,
574
- automaticLayout: true,
575
- wordWrap: 'on',
576
- }}
577
- loading={
578
- <div className="flex h-full items-center justify-center text-slate-500 dark:text-slate-400">
579
- Loading editor...
580
- </div>
581
- }
582
- />
583
- </div>
584
- ) : isTranspiling ? (
585
- <div className="py-12 text-center text-sm text-slate-500 dark:text-slate-400">
586
- Generating code...
587
- </div>
588
- ) : selectedTranspilerName ? (
589
- <div className="py-12 text-center text-sm text-slate-500 dark:text-slate-400">
590
- Generated code will appear here.
591
- </div>
592
- ) : (
593
- <div className="py-12 text-center text-sm text-slate-500 dark:text-slate-400">
594
- Select a transpiler to generate code.
595
- </div>
596
- )}
597
- </div>
598
- </div>
599
- </div>
600
- </AgentCodePageSection>
54
+ <SourceBookSection agentName={agentName} agentSource={agentSource} />
55
+ <GeneratedHarnessSection state={state} exportWarnings={exportWarnings} />
601
56
  </div>
602
57
  </div>
603
58
  </div>
@@ -0,0 +1,84 @@
1
+ 'use client';
2
+
3
+ import type { AgentBasicInformation, string_url } from '@promptbook-local/types';
4
+ import { CodeIcon } from 'lucide-react';
5
+ import { resolveAgentAvatarImageUrl } from '../../../../../../../src/utils/agents/resolveAgentAvatarImageUrl';
6
+
7
+ /**
8
+ * Props for the export page header.
9
+ *
10
+ * @private type of `<AgentCodePageHeader/>`
11
+ */
12
+ type AgentCodePageHeaderProps = {
13
+ /**
14
+ * Routed agent name.
15
+ */
16
+ readonly agentName: string;
17
+
18
+ /**
19
+ * Loaded agent profile, or `null` while loading.
20
+ */
21
+ readonly agentProfile: AgentBasicInformation | null;
22
+
23
+ /**
24
+ * Base URL of the Agents Server.
25
+ */
26
+ readonly publicUrl: string_url;
27
+ };
28
+
29
+ /**
30
+ * Renders the agent identity header (avatar + title) for the export-as-transpiled-code page.
31
+ *
32
+ * @private internal component of `<AgentCodePageClient/>`
33
+ */
34
+ export function AgentCodePageHeader({ agentName, agentProfile, publicUrl }: AgentCodePageHeaderProps) {
35
+ const agentDisplayName = agentProfile?.meta.fullname || agentName;
36
+ const agentAvatarSource = resolveAgentAvatarSource({ agentName, agentProfile, publicUrl });
37
+
38
+ return (
39
+ <div className="flex flex-col gap-4 border-b border-slate-200 px-6 py-6 sm:flex-row sm:items-center dark:border-slate-800">
40
+ {/* eslint-disable-next-line @next/next/no-img-element */}
41
+ <img
42
+ src={agentAvatarSource}
43
+ alt={agentDisplayName}
44
+ className="agent-avatar-pixelated h-16 w-16 rounded-full border-2 border-slate-200 object-cover dark:border-slate-700"
45
+ />
46
+
47
+ <div className="min-w-0 flex-1">
48
+ <h1 className="truncate text-2xl font-bold text-slate-900 dark:text-slate-100">{agentDisplayName}</h1>
49
+ <p className="mt-1 flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
50
+ <CodeIcon className="h-4 w-4" />
51
+ Export as transpiled code / agent harness
52
+ </p>
53
+ </div>
54
+ </div>
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Resolves the avatar image URL for the current agent header.
60
+ *
61
+ * @param options - Agent identity and loaded profile data.
62
+ * @returns Avatar URL preferred by the profile, falling back to the default generated avatar.
63
+ *
64
+ * @private function of `<AgentCodePageHeader/>`
65
+ */
66
+ function resolveAgentAvatarSource(options: {
67
+ readonly agentName: string;
68
+ readonly agentProfile: AgentBasicInformation | null;
69
+ readonly publicUrl: string_url;
70
+ }): string {
71
+ const { agentName, agentProfile, publicUrl } = options;
72
+ const fallbackIdentifier = agentProfile?.permanentId || agentName;
73
+
74
+ if (!agentProfile) {
75
+ return `/agents/${encodeURIComponent(fallbackIdentifier)}/images/default-avatar.png`;
76
+ }
77
+
78
+ return (
79
+ resolveAgentAvatarImageUrl({
80
+ agent: agentProfile,
81
+ baseUrl: publicUrl,
82
+ }) || `/agents/${encodeURIComponent(fallbackIdentifier)}/images/default-avatar.png`
83
+ );
84
+ }