@promptbook/cli 0.112.0-132 → 0.112.0-133
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.
- package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +4 -1
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +3 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackTable.tsx +13 -8
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +3 -0
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryTable.tsx +13 -8
- package/apps/agents-server/src/app/admin/custom-css/CustomCssClient.tsx +4 -0
- package/apps/agents-server/src/app/admin/custom-css/CustomCssEditorPanel.tsx +7 -1
- package/apps/agents-server/src/app/admin/custom-css/CustomCssFilesPanel.tsx +7 -1
- package/apps/agents-server/src/app/admin/custom-js/CustomJsClient.tsx +4 -0
- package/apps/agents-server/src/app/admin/custom-js/CustomJsEditorPanel.tsx +7 -1
- package/apps/agents-server/src/app/admin/custom-js/CustomJsFilesPanel.tsx +7 -1
- package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +4 -0
- package/apps/agents-server/src/app/admin/files/FilesGalleryGrid.tsx +9 -11
- package/apps/agents-server/src/app/admin/files/FilesGalleryTable.tsx +9 -8
- package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +4 -0
- package/apps/agents-server/src/app/admin/images/ImagesGalleryGrid.tsx +16 -11
- package/apps/agents-server/src/app/admin/images/ImagesGalleryTable.tsx +18 -9
- package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +9 -9
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +7 -6
- package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +11 -10
- package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +4 -2
- package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +14 -15
- package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +4 -1
- package/apps/agents-server/src/app/admin/task-manager/useTaskManagerState.ts +6 -9
- package/apps/agents-server/src/app/admin/update/CustomCommitPicker.tsx +258 -0
- package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +305 -108
- package/apps/agents-server/src/app/admin/usage/UsageClient.tsx +4 -2
- package/apps/agents-server/src/app/admin/usage/UsageClientAnalyticsPanels.tsx +6 -4
- package/apps/agents-server/src/app/admin/usage/UsageClientFormatting.ts +6 -12
- package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +6 -3
- package/apps/agents-server/src/app/admin/usage/useUsageClientState.ts +12 -4
- package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +8 -3
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +6 -20
- package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorHistory.ts +9 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistoryClientState.ts +2 -15
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +6 -1
- package/apps/agents-server/src/app/agents/[agentName]/integration/CalendarIntegrationSection.tsx +23 -5
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +9 -4
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +4 -1
- package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +10 -3
- package/apps/agents-server/src/app/api/admin/update/commits/route.ts +35 -0
- package/apps/agents-server/src/app/api/admin/update/route.ts +10 -2
- package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +14 -2
- package/apps/agents-server/src/app/dashboard/page.tsx +11 -16
- package/apps/agents-server/src/app/swagger/SwaggerApiKeysPanel.tsx +4 -1
- package/apps/agents-server/src/app/system/user-memory/UserMemoryClient.tsx +4 -2
- package/apps/agents-server/src/app/system/user-wallet/UserWalletClient.tsx +3 -0
- package/apps/agents-server/src/app/system/user-wallet/UserWalletRecordsTable.tsx +5 -1
- package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorClient.tsx +3 -0
- package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorSidebar.tsx +7 -8
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -2
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +4 -2
- package/apps/agents-server/src/tools/createAgentProgressTools.ts +8 -6
- package/apps/agents-server/src/utils/localization/formatServerLanguageHumanReadableDate.ts +70 -0
- package/apps/agents-server/src/utils/localization/getRequestServerLanguage.ts +32 -0
- package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +17 -0
- package/apps/agents-server/src/utils/userChat/createRunUserChatJobPersistenceController.ts +0 -21
- package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +31 -0
- package/apps/agents-server/src/utils/userChat/retryUserChatJob.ts +0 -4
- package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +29 -0
- package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +2 -2
- package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +99 -401
- package/apps/agents-server/src/utils/vpsSelfUpdate.ts +625 -51
- package/esm/index.es.js +252 -41
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
- package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
- package/esm/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
- package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
- package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
- package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
- package/esm/src/book-3.0/cliAgentEnv.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/book-3.0/cliAgentEnv.ts +1 -1
- package/src/cli/cli-commands/agents-server/startAgentsServer.ts +1 -1
- package/src/cli/cli-commands/coder/ThinkingLevel.ts +3 -1
- package/src/other/templates/getTemplatesPipelineCollection.ts +711 -791
- package/src/version.ts +2 -2
- package/src/versions.txt +1 -0
- package/umd/index.umd.js +254 -44
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
- package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
- package/umd/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
- package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
- package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
- package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
- package/umd/src/book-3.0/cliAgentEnv.d.ts +1 -1
- package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
- package/umd/src/version.d.ts +1 -1
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
CheckCircle2,
|
|
5
|
+
ChevronDown,
|
|
6
|
+
ChevronUp,
|
|
7
|
+
Download,
|
|
8
|
+
Loader2,
|
|
9
|
+
RefreshCcw,
|
|
10
|
+
Rocket,
|
|
11
|
+
Server,
|
|
12
|
+
Settings2,
|
|
13
|
+
TriangleAlert,
|
|
14
|
+
} from 'lucide-react';
|
|
4
15
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
5
16
|
import { AdminXtermTerminal } from '../../../components/AdminTerminal/AdminXtermTerminal';
|
|
6
17
|
import { Card } from '../../../components/Homepage/Card';
|
|
18
|
+
import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
|
|
19
|
+
import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
|
|
20
|
+
import { createServerLanguageMoment } from '../../../utils/localization/createServerLanguageMoment';
|
|
21
|
+
import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
|
|
22
|
+
import { CustomCommitPicker, type CustomCommitPickerCandidate } from './CustomCommitPicker';
|
|
7
23
|
|
|
8
24
|
/**
|
|
9
25
|
* Browser-safe environment option returned by the update API.
|
|
@@ -13,6 +29,7 @@ type UpdateEnvironmentOption = {
|
|
|
13
29
|
readonly branch: string;
|
|
14
30
|
readonly label: string;
|
|
15
31
|
readonly description: string;
|
|
32
|
+
readonly isCustom: boolean;
|
|
16
33
|
};
|
|
17
34
|
|
|
18
35
|
/**
|
|
@@ -46,9 +63,15 @@ type UpdateOverview = {
|
|
|
46
63
|
readonly currentCommitSha: string | null;
|
|
47
64
|
readonly currentCommitShortSha: string | null;
|
|
48
65
|
readonly currentCommitMessage: string | null;
|
|
66
|
+
readonly currentCommitDate: string | null;
|
|
49
67
|
readonly latestRemoteCommitSha: string | null;
|
|
50
68
|
readonly latestRemoteCommitShortSha: string | null;
|
|
69
|
+
readonly latestRemoteCommitDate: string | null;
|
|
70
|
+
readonly commitsBehindCount: number | null;
|
|
51
71
|
readonly isUpdateAvailable: boolean;
|
|
72
|
+
readonly originRepositoryUrl: string;
|
|
73
|
+
readonly isOriginRepositoryDefault: boolean;
|
|
74
|
+
readonly defaultOriginRepositoryUrl: string;
|
|
52
75
|
readonly job: UpdateJobSnapshot;
|
|
53
76
|
readonly error?: string;
|
|
54
77
|
};
|
|
@@ -57,16 +80,18 @@ type UpdateOverview = {
|
|
|
57
80
|
* Client UI for standalone VPS branch-aware self-updates.
|
|
58
81
|
*/
|
|
59
82
|
export function UpdateClient() {
|
|
83
|
+
const { language } = useServerLanguage();
|
|
60
84
|
const [overview, setOverview] = useState<UpdateOverview | null>(null);
|
|
61
85
|
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string>('');
|
|
86
|
+
const [customRef, setCustomRef] = useState<string>('');
|
|
87
|
+
const [customCandidate, setCustomCandidate] = useState<CustomCommitPickerCandidate | null>(null);
|
|
88
|
+
const [originRepositoryUrlOverride, setOriginRepositoryUrlOverride] = useState<string>('');
|
|
89
|
+
const [isAdvancedExpanded, setIsAdvancedExpanded] = useState<boolean>(false);
|
|
62
90
|
const [isLoading, setIsLoading] = useState(true);
|
|
63
91
|
const [isStartingUpdate, setIsStartingUpdate] = useState(false);
|
|
64
92
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
|
65
93
|
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
|
66
94
|
|
|
67
|
-
/**
|
|
68
|
-
* Loads the latest self-update overview from the server.
|
|
69
|
-
*/
|
|
70
95
|
const loadOverview = useCallback(
|
|
71
96
|
async (options?: { readonly isSilent?: boolean; readonly isRestartExpected?: boolean }): Promise<void> => {
|
|
72
97
|
try {
|
|
@@ -86,6 +111,7 @@ export function UpdateClient() {
|
|
|
86
111
|
setSelectedEnvironmentId(
|
|
87
112
|
(currentSelectedEnvironmentId) => currentSelectedEnvironmentId || payload.currentEnvironment.id,
|
|
88
113
|
);
|
|
114
|
+
setOriginRepositoryUrlOverride((currentValue) => currentValue || payload.originRepositoryUrl);
|
|
89
115
|
if (payload.job.status === 'succeeded') {
|
|
90
116
|
setSuccessMessage(getUpdateJobSuccessMessage(payload.job));
|
|
91
117
|
} else if (payload.job.status === 'failed') {
|
|
@@ -131,16 +157,18 @@ export function UpdateClient() {
|
|
|
131
157
|
null,
|
|
132
158
|
[overview, selectedEnvironmentId],
|
|
133
159
|
);
|
|
160
|
+
const isCustomEnvironmentSelected = selectedEnvironment?.isCustom === true;
|
|
134
161
|
const isEnvironmentSwitchRequired =
|
|
135
162
|
Boolean(selectedEnvironment) && selectedEnvironment?.id !== overview?.currentEnvironment.id;
|
|
136
163
|
const isUpdateRunning = overview?.job.status === 'running';
|
|
137
164
|
const updateTerminalId = `standalone-vps-update:${overview?.job.startedAt || overview?.job.finishedAt || overview?.job.status || 'loading'}`;
|
|
138
165
|
const updateTerminalEmptyState =
|
|
139
166
|
isLoading && !overview ? 'Loading update log...' : 'No persisted update log output yet.';
|
|
167
|
+
const isCustomRefRelease = isCustomEnvironmentSelected ? customCandidate?.isReleaseTag === true : true;
|
|
168
|
+
const isCustomRefMissing = isCustomEnvironmentSelected && !customRef.trim();
|
|
169
|
+
const isOriginOverrideChanged =
|
|
170
|
+
overview !== null && originRepositoryUrlOverride.trim() !== overview.originRepositoryUrl;
|
|
140
171
|
|
|
141
|
-
/**
|
|
142
|
-
* Starts one detached update run for the selected environment.
|
|
143
|
-
*/
|
|
144
172
|
async function startUpdate(): Promise<void> {
|
|
145
173
|
if (!selectedEnvironment) {
|
|
146
174
|
return;
|
|
@@ -158,6 +186,8 @@ export function UpdateClient() {
|
|
|
158
186
|
},
|
|
159
187
|
body: JSON.stringify({
|
|
160
188
|
environment: selectedEnvironment.id,
|
|
189
|
+
customRef: isCustomEnvironmentSelected ? customRef.trim() : null,
|
|
190
|
+
originRepositoryUrl: isOriginOverrideChanged ? originRepositoryUrlOverride.trim() : null,
|
|
161
191
|
}),
|
|
162
192
|
});
|
|
163
193
|
const payload = (await response.json()) as UpdateOverview;
|
|
@@ -168,9 +198,11 @@ export function UpdateClient() {
|
|
|
168
198
|
|
|
169
199
|
setOverview(payload);
|
|
170
200
|
setSuccessMessage(
|
|
171
|
-
|
|
172
|
-
? `
|
|
173
|
-
:
|
|
201
|
+
isCustomEnvironmentSelected
|
|
202
|
+
? `Started the standalone VPS update to custom ref ${customRef.trim()}.`
|
|
203
|
+
: isEnvironmentSwitchRequired
|
|
204
|
+
? `Switched to ${selectedEnvironment.label} and started the standalone VPS update.`
|
|
205
|
+
: 'Standalone VPS update started.',
|
|
174
206
|
);
|
|
175
207
|
} catch (error) {
|
|
176
208
|
setErrorMessage(error instanceof Error ? error.message : 'Failed to start the update.');
|
|
@@ -185,8 +217,8 @@ export function UpdateClient() {
|
|
|
185
217
|
<div>
|
|
186
218
|
<h1 className="text-3xl font-light text-gray-900">Update</h1>
|
|
187
219
|
<p className="mt-1 max-w-3xl text-sm text-gray-500">
|
|
188
|
-
Switch the standalone VPS between
|
|
189
|
-
Promptbook checkout with one click.
|
|
220
|
+
Switch the standalone VPS between Live, Preview, Production, LTS, or a custom ref, and update
|
|
221
|
+
the managed Promptbook checkout with one click.
|
|
190
222
|
</p>
|
|
191
223
|
</div>
|
|
192
224
|
|
|
@@ -220,77 +252,7 @@ export function UpdateClient() {
|
|
|
220
252
|
|
|
221
253
|
<div className="grid gap-4 lg:grid-cols-[1.4fr_1fr]">
|
|
222
254
|
<Card className="hover:border-gray-200 hover:shadow-md">
|
|
223
|
-
<
|
|
224
|
-
<div className="flex items-start gap-3">
|
|
225
|
-
<Server className="mt-0.5 h-5 w-5 text-blue-600" />
|
|
226
|
-
<div>
|
|
227
|
-
<h2 className="text-lg font-semibold text-slate-900">Current deployment</h2>
|
|
228
|
-
<p className="mt-1 text-sm text-slate-500">
|
|
229
|
-
The server currently tracks the <span className="font-medium">{overview?.currentEnvironment.label || 'Production'}</span>{' '}
|
|
230
|
-
environment on branch <span className="font-mono">{overview?.currentEnvironment.branch || 'production'}</span>.
|
|
231
|
-
</p>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<dl className="grid gap-4 text-sm text-slate-600 sm:grid-cols-2">
|
|
236
|
-
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
237
|
-
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Branch</dt>
|
|
238
|
-
<dd className="mt-1 font-mono text-slate-900">
|
|
239
|
-
{overview?.currentEnvironment.branch || 'production'}
|
|
240
|
-
</dd>
|
|
241
|
-
</div>
|
|
242
|
-
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
243
|
-
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
244
|
-
Deployed commit
|
|
245
|
-
</dt>
|
|
246
|
-
<dd className="mt-1 font-mono text-slate-900">
|
|
247
|
-
{overview?.currentCommitShortSha || 'Unknown'}
|
|
248
|
-
</dd>
|
|
249
|
-
</div>
|
|
250
|
-
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
251
|
-
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
252
|
-
Latest remote commit
|
|
253
|
-
</dt>
|
|
254
|
-
<dd className="mt-1 font-mono text-slate-900">
|
|
255
|
-
{overview?.latestRemoteCommitShortSha || 'Unknown'}
|
|
256
|
-
</dd>
|
|
257
|
-
</div>
|
|
258
|
-
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
259
|
-
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
260
|
-
Update availability
|
|
261
|
-
</dt>
|
|
262
|
-
<dd className="mt-1 flex items-center gap-2 text-slate-900">
|
|
263
|
-
{overview?.isUpdateAvailable ? (
|
|
264
|
-
<>
|
|
265
|
-
<TriangleAlert className="h-4 w-4 text-amber-500" />
|
|
266
|
-
New commit available
|
|
267
|
-
</>
|
|
268
|
-
) : (
|
|
269
|
-
<>
|
|
270
|
-
<CheckCircle2 className="h-4 w-4 text-emerald-600" />
|
|
271
|
-
Up to date
|
|
272
|
-
</>
|
|
273
|
-
)}
|
|
274
|
-
</dd>
|
|
275
|
-
</div>
|
|
276
|
-
</dl>
|
|
277
|
-
|
|
278
|
-
{overview?.currentCommitMessage && (
|
|
279
|
-
<div className="rounded-xl border border-slate-200 bg-white px-4 py-3 text-sm text-slate-600">
|
|
280
|
-
<div className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
281
|
-
Current commit message
|
|
282
|
-
</div>
|
|
283
|
-
<div className="mt-1 text-slate-900">{overview.currentCommitMessage}</div>
|
|
284
|
-
</div>
|
|
285
|
-
)}
|
|
286
|
-
|
|
287
|
-
{overview?.repositoryDirectory && (
|
|
288
|
-
<div className="text-xs text-slate-500">
|
|
289
|
-
Managed repository:
|
|
290
|
-
<span className="ml-2 font-mono text-slate-700">{overview.repositoryDirectory}</span>
|
|
291
|
-
</div>
|
|
292
|
-
)}
|
|
293
|
-
</div>
|
|
255
|
+
<CurrentDeploymentCard overview={overview} language={language} />
|
|
294
256
|
</Card>
|
|
295
257
|
|
|
296
258
|
<Card className="hover:border-gray-200 hover:shadow-md">
|
|
@@ -328,13 +290,48 @@ export function UpdateClient() {
|
|
|
328
290
|
</span>
|
|
329
291
|
)}
|
|
330
292
|
</div>
|
|
331
|
-
|
|
293
|
+
{environment.branch && (
|
|
294
|
+
<div className="mt-1 font-mono text-xs">{environment.branch}</div>
|
|
295
|
+
)}
|
|
332
296
|
<div className="mt-2 text-sm opacity-80">{environment.description}</div>
|
|
333
297
|
</button>
|
|
334
298
|
);
|
|
335
299
|
})}
|
|
336
300
|
</div>
|
|
337
301
|
|
|
302
|
+
{isCustomEnvironmentSelected && overview?.isAvailable && (
|
|
303
|
+
<div className="space-y-3 rounded-xl border border-blue-200 bg-blue-50/40 p-4">
|
|
304
|
+
<CustomCommitPicker
|
|
305
|
+
language={language}
|
|
306
|
+
selectedRef={customRef}
|
|
307
|
+
onSelectRef={(nextRef, matchedCandidate) => {
|
|
308
|
+
setCustomRef(nextRef);
|
|
309
|
+
setCustomCandidate(matchedCandidate);
|
|
310
|
+
}}
|
|
311
|
+
isDisabled={isUpdateRunning || isStartingUpdate}
|
|
312
|
+
/>
|
|
313
|
+
|
|
314
|
+
{customRef.trim() && !isCustomRefRelease && (
|
|
315
|
+
<div className="flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800">
|
|
316
|
+
<TriangleAlert className="mt-0.5 h-4 w-4 shrink-0" />
|
|
317
|
+
<div>
|
|
318
|
+
<strong>Heads up:</strong> the selected ref is not a release tag, so it may
|
|
319
|
+
be unstable and was not validated by the regular release pipeline.
|
|
320
|
+
</div>
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
</div>
|
|
324
|
+
)}
|
|
325
|
+
|
|
326
|
+
<AdvancedOriginRepositoryPanel
|
|
327
|
+
originRepositoryUrlOverride={originRepositoryUrlOverride}
|
|
328
|
+
onChange={setOriginRepositoryUrlOverride}
|
|
329
|
+
overview={overview}
|
|
330
|
+
isExpanded={isAdvancedExpanded}
|
|
331
|
+
onToggleExpanded={() => setIsAdvancedExpanded((current) => !current)}
|
|
332
|
+
isDisabled={isUpdateRunning || isStartingUpdate}
|
|
333
|
+
/>
|
|
334
|
+
|
|
338
335
|
<button
|
|
339
336
|
type="button"
|
|
340
337
|
onClick={() => void startUpdate()}
|
|
@@ -343,20 +340,28 @@ export function UpdateClient() {
|
|
|
343
340
|
!selectedEnvironment ||
|
|
344
341
|
isUpdateRunning ||
|
|
345
342
|
isStartingUpdate ||
|
|
346
|
-
|
|
343
|
+
isCustomRefMissing ||
|
|
344
|
+
(!isCustomEnvironmentSelected &&
|
|
345
|
+
!isEnvironmentSwitchRequired &&
|
|
346
|
+
!overview?.isUpdateAvailable &&
|
|
347
|
+
!isOriginOverrideChanged)
|
|
347
348
|
}
|
|
348
349
|
className="inline-flex w-full items-center justify-center gap-2 rounded-md bg-blue-600 px-4 py-3 text-sm font-semibold text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
|
|
349
350
|
>
|
|
350
351
|
{isStartingUpdate || isUpdateRunning ? (
|
|
351
352
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
353
|
+
) : isCustomEnvironmentSelected ? (
|
|
354
|
+
<Rocket className="h-4 w-4" />
|
|
352
355
|
) : isEnvironmentSwitchRequired ? (
|
|
353
356
|
<Rocket className="h-4 w-4" />
|
|
354
357
|
) : (
|
|
355
358
|
<Download className="h-4 w-4" />
|
|
356
359
|
)}
|
|
357
|
-
{
|
|
358
|
-
? `
|
|
359
|
-
:
|
|
360
|
+
{isCustomEnvironmentSelected
|
|
361
|
+
? `Update to ${customRef.trim() || 'custom ref'}`
|
|
362
|
+
: isEnvironmentSwitchRequired
|
|
363
|
+
? `Switch to ${selectedEnvironment?.label || 'selected environment'} and update`
|
|
364
|
+
: 'Update to latest commit'}
|
|
360
365
|
</button>
|
|
361
366
|
</div>
|
|
362
367
|
</Card>
|
|
@@ -377,10 +382,10 @@ export function UpdateClient() {
|
|
|
377
382
|
overview?.job.status === 'running'
|
|
378
383
|
? 'border-blue-200 bg-blue-50 text-blue-700'
|
|
379
384
|
: overview?.job.status === 'failed'
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
385
|
+
? 'border-rose-200 bg-rose-50 text-rose-700'
|
|
386
|
+
: overview?.job.status === 'succeeded'
|
|
387
|
+
? 'border-emerald-200 bg-emerald-50 text-emerald-700'
|
|
388
|
+
: 'border-slate-200 bg-slate-50 text-slate-500'
|
|
384
389
|
}`}
|
|
385
390
|
>
|
|
386
391
|
{overview?.job.status || 'idle'}
|
|
@@ -390,7 +395,9 @@ export function UpdateClient() {
|
|
|
390
395
|
<dl className="grid gap-4 text-sm text-slate-600 md:grid-cols-2 xl:grid-cols-4">
|
|
391
396
|
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
392
397
|
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Target</dt>
|
|
393
|
-
<dd className="mt-1 text-slate-900">
|
|
398
|
+
<dd className="mt-1 text-slate-900">
|
|
399
|
+
{overview?.job.targetEnvironment.label || 'Production'}
|
|
400
|
+
</dd>
|
|
394
401
|
</div>
|
|
395
402
|
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
396
403
|
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Step</dt>
|
|
@@ -398,11 +405,15 @@ export function UpdateClient() {
|
|
|
398
405
|
</div>
|
|
399
406
|
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
400
407
|
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Started</dt>
|
|
401
|
-
<dd className="mt-1 text-slate-900">
|
|
408
|
+
<dd className="mt-1 text-slate-900">
|
|
409
|
+
{formatHumanReadableTimestamp(overview?.job.startedAt, language)}
|
|
410
|
+
</dd>
|
|
402
411
|
</div>
|
|
403
412
|
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
404
413
|
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Finished</dt>
|
|
405
|
-
<dd className="mt-1 text-slate-900">
|
|
414
|
+
<dd className="mt-1 text-slate-900">
|
|
415
|
+
{formatHumanReadableTimestamp(overview?.job.finishedAt, language)}
|
|
416
|
+
</dd>
|
|
406
417
|
</div>
|
|
407
418
|
</dl>
|
|
408
419
|
|
|
@@ -436,6 +447,197 @@ export function UpdateClient() {
|
|
|
436
447
|
);
|
|
437
448
|
}
|
|
438
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Current-deployment metrics card, including the new commits-behind and time-behind info.
|
|
452
|
+
*
|
|
453
|
+
* @private internal component of `<UpdateClient/>`
|
|
454
|
+
*/
|
|
455
|
+
function CurrentDeploymentCard({
|
|
456
|
+
overview,
|
|
457
|
+
language,
|
|
458
|
+
}: {
|
|
459
|
+
readonly overview: UpdateOverview | null;
|
|
460
|
+
readonly language: ServerLanguageCode;
|
|
461
|
+
}) {
|
|
462
|
+
const currentCommitDateLabel = formatHumanReadableTimestamp(overview?.currentCommitDate ?? null, language);
|
|
463
|
+
const latestRemoteCommitDateLabel = formatHumanReadableTimestamp(
|
|
464
|
+
overview?.latestRemoteCommitDate ?? null,
|
|
465
|
+
language,
|
|
466
|
+
);
|
|
467
|
+
const driftLabel = useMemo(
|
|
468
|
+
() => buildDeploymentDriftLabel(overview, language),
|
|
469
|
+
[overview, language],
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
return (
|
|
473
|
+
<div className="space-y-4">
|
|
474
|
+
<div className="flex items-start gap-3">
|
|
475
|
+
<Server className="mt-0.5 h-5 w-5 text-blue-600" />
|
|
476
|
+
<div>
|
|
477
|
+
<h2 className="text-lg font-semibold text-slate-900">Current deployment</h2>
|
|
478
|
+
<p className="mt-1 text-sm text-slate-500">
|
|
479
|
+
The server currently tracks the{' '}
|
|
480
|
+
<span className="font-medium">
|
|
481
|
+
{overview?.currentEnvironment.label || 'Production'}
|
|
482
|
+
</span>{' '}
|
|
483
|
+
environment on branch{' '}
|
|
484
|
+
<span className="font-mono">{overview?.currentEnvironment.branch || 'production'}</span>.
|
|
485
|
+
</p>
|
|
486
|
+
</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<dl className="grid gap-4 text-sm text-slate-600 sm:grid-cols-2">
|
|
490
|
+
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
491
|
+
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Branch</dt>
|
|
492
|
+
<dd className="mt-1 font-mono text-slate-900">
|
|
493
|
+
{overview?.currentEnvironment.branch || 'production'}
|
|
494
|
+
</dd>
|
|
495
|
+
</div>
|
|
496
|
+
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
497
|
+
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">Deployed commit</dt>
|
|
498
|
+
<dd className="mt-1 font-mono text-slate-900">{overview?.currentCommitShortSha || 'Unknown'}</dd>
|
|
499
|
+
<div className="mt-1 text-xs text-slate-500">{currentCommitDateLabel}</div>
|
|
500
|
+
</div>
|
|
501
|
+
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
502
|
+
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
503
|
+
Latest remote commit
|
|
504
|
+
</dt>
|
|
505
|
+
<dd className="mt-1 font-mono text-slate-900">
|
|
506
|
+
{overview?.latestRemoteCommitShortSha || 'Unknown'}
|
|
507
|
+
</dd>
|
|
508
|
+
<div className="mt-1 text-xs text-slate-500">{latestRemoteCommitDateLabel}</div>
|
|
509
|
+
</div>
|
|
510
|
+
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3">
|
|
511
|
+
<dt className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
512
|
+
Update availability
|
|
513
|
+
</dt>
|
|
514
|
+
<dd className="mt-1 flex items-center gap-2 text-slate-900">
|
|
515
|
+
{overview?.isUpdateAvailable ? (
|
|
516
|
+
<>
|
|
517
|
+
<TriangleAlert className="h-4 w-4 text-amber-500" />
|
|
518
|
+
{driftLabel}
|
|
519
|
+
</>
|
|
520
|
+
) : (
|
|
521
|
+
<>
|
|
522
|
+
<CheckCircle2 className="h-4 w-4 text-emerald-600" />
|
|
523
|
+
Up to date
|
|
524
|
+
</>
|
|
525
|
+
)}
|
|
526
|
+
</dd>
|
|
527
|
+
</div>
|
|
528
|
+
</dl>
|
|
529
|
+
|
|
530
|
+
{overview?.currentCommitMessage && (
|
|
531
|
+
<div className="rounded-xl border border-slate-200 bg-white px-4 py-3 text-sm text-slate-600">
|
|
532
|
+
<div className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
533
|
+
Current commit message
|
|
534
|
+
</div>
|
|
535
|
+
<div className="mt-1 text-slate-900">{overview.currentCommitMessage}</div>
|
|
536
|
+
</div>
|
|
537
|
+
)}
|
|
538
|
+
|
|
539
|
+
{overview?.repositoryDirectory && (
|
|
540
|
+
<div className="text-xs text-slate-500">
|
|
541
|
+
Managed repository:
|
|
542
|
+
<span className="ml-2 font-mono text-slate-700">{overview.repositoryDirectory}</span>
|
|
543
|
+
</div>
|
|
544
|
+
)}
|
|
545
|
+
</div>
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Collapsible advanced panel for overriding the upstream repository URL.
|
|
551
|
+
*
|
|
552
|
+
* @private internal component of `<UpdateClient/>`
|
|
553
|
+
*/
|
|
554
|
+
function AdvancedOriginRepositoryPanel({
|
|
555
|
+
originRepositoryUrlOverride,
|
|
556
|
+
onChange,
|
|
557
|
+
overview,
|
|
558
|
+
isExpanded,
|
|
559
|
+
onToggleExpanded,
|
|
560
|
+
isDisabled,
|
|
561
|
+
}: {
|
|
562
|
+
readonly originRepositoryUrlOverride: string;
|
|
563
|
+
readonly onChange: (nextValue: string) => void;
|
|
564
|
+
readonly overview: UpdateOverview | null;
|
|
565
|
+
readonly isExpanded: boolean;
|
|
566
|
+
readonly onToggleExpanded: () => void;
|
|
567
|
+
readonly isDisabled: boolean;
|
|
568
|
+
}) {
|
|
569
|
+
return (
|
|
570
|
+
<div className="rounded-xl border border-slate-200 bg-white">
|
|
571
|
+
<button
|
|
572
|
+
type="button"
|
|
573
|
+
onClick={onToggleExpanded}
|
|
574
|
+
className="flex w-full items-center justify-between gap-3 px-4 py-3 text-sm font-semibold text-slate-700"
|
|
575
|
+
>
|
|
576
|
+
<span className="inline-flex items-center gap-2">
|
|
577
|
+
<Settings2 className="h-4 w-4" />
|
|
578
|
+
Advanced
|
|
579
|
+
</span>
|
|
580
|
+
{isExpanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
|
|
581
|
+
</button>
|
|
582
|
+
{isExpanded && (
|
|
583
|
+
<div className="space-y-3 border-t border-slate-100 px-4 py-3 text-sm text-slate-600">
|
|
584
|
+
<label className="block">
|
|
585
|
+
<span className="text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
586
|
+
Upstream repository URL
|
|
587
|
+
</span>
|
|
588
|
+
<input
|
|
589
|
+
type="url"
|
|
590
|
+
value={originRepositoryUrlOverride}
|
|
591
|
+
onChange={(event) => onChange(event.target.value)}
|
|
592
|
+
disabled={isDisabled}
|
|
593
|
+
placeholder={overview?.defaultOriginRepositoryUrl || ''}
|
|
594
|
+
className="mt-1 w-full rounded-md border border-slate-300 bg-white px-3 py-2 font-mono text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-60"
|
|
595
|
+
/>
|
|
596
|
+
</label>
|
|
597
|
+
<p className="text-xs text-slate-500">
|
|
598
|
+
Defaults to <span className="font-mono">{overview?.defaultOriginRepositoryUrl}</span>. Only
|
|
599
|
+
change this if you self-host a fork of the original Promptbook repository — the new value is
|
|
600
|
+
persisted in the standalone VPS <code>.env</code>.
|
|
601
|
+
</p>
|
|
602
|
+
{overview && !overview.isOriginRepositoryDefault && (
|
|
603
|
+
<div className="rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800">
|
|
604
|
+
<strong>Custom origin active:</strong> updates currently pull from{' '}
|
|
605
|
+
<span className="font-mono">{overview.originRepositoryUrl}</span>.
|
|
606
|
+
</div>
|
|
607
|
+
)}
|
|
608
|
+
</div>
|
|
609
|
+
)}
|
|
610
|
+
</div>
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Builds the "behind by …" label combining commits-behind and time-behind info.
|
|
616
|
+
*
|
|
617
|
+
* @param overview - Current overview snapshot.
|
|
618
|
+
* @param language - Active UI language for moment localization.
|
|
619
|
+
* @returns Human-readable label or fallback text.
|
|
620
|
+
*/
|
|
621
|
+
function buildDeploymentDriftLabel(overview: UpdateOverview | null, language: ServerLanguageCode): string {
|
|
622
|
+
if (!overview) {
|
|
623
|
+
return 'New commit available';
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const commitsBehindLabel =
|
|
627
|
+
overview.commitsBehindCount !== null && overview.commitsBehindCount > 0
|
|
628
|
+
? `${overview.commitsBehindCount} commit${overview.commitsBehindCount === 1 ? '' : 's'} behind`
|
|
629
|
+
: 'New commit available';
|
|
630
|
+
|
|
631
|
+
if (!overview.currentCommitDate || !overview.latestRemoteCommitDate) {
|
|
632
|
+
return commitsBehindLabel;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const currentMoment = createServerLanguageMoment(overview.currentCommitDate, language);
|
|
636
|
+
const latestMoment = createServerLanguageMoment(overview.latestRemoteCommitDate, language);
|
|
637
|
+
const timeBehindLabel = currentMoment.from(latestMoment, true);
|
|
638
|
+
return `${commitsBehindLabel} · ${timeBehindLabel} behind`;
|
|
639
|
+
}
|
|
640
|
+
|
|
439
641
|
/**
|
|
440
642
|
* Builds the success message for a finished standalone VPS update.
|
|
441
643
|
*
|
|
@@ -457,20 +659,15 @@ function getUpdateJobFailureMessage(job: UpdateJobSnapshot): string {
|
|
|
457
659
|
}
|
|
458
660
|
|
|
459
661
|
/**
|
|
460
|
-
* Formats optional timestamps for the status cards.
|
|
662
|
+
* Formats optional timestamps for the status cards using `moment.js` with the active UI language.
|
|
461
663
|
*
|
|
462
664
|
* @param value - ISO timestamp or `null`.
|
|
463
|
-
* @
|
|
665
|
+
* @param language - Active UI language code.
|
|
666
|
+
* @returns Localized human-friendly timestamp or fallback text.
|
|
464
667
|
*/
|
|
465
|
-
function
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const date = new Date(value);
|
|
471
|
-
if (Number.isNaN(date.getTime())) {
|
|
472
|
-
return value;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
return `${date.toLocaleString()} (${value})`;
|
|
668
|
+
function formatHumanReadableTimestamp(value: string | null | undefined, language: ServerLanguageCode): string {
|
|
669
|
+
return formatServerLanguageHumanReadableDate(value, language, {
|
|
670
|
+
fallbackLabel: 'Not available',
|
|
671
|
+
isExactDateIncluded: true,
|
|
672
|
+
});
|
|
476
673
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAgentNaming } from '@/src/components/AgentNaming/AgentNamingContext';
|
|
4
4
|
import { Card } from '@/src/components/Homepage/Card';
|
|
5
|
+
import { useServerLanguage } from '@/src/components/ServerLanguage/ServerLanguageProvider';
|
|
5
6
|
import {
|
|
6
7
|
type UsageActorType,
|
|
7
8
|
type UsageAgentOption,
|
|
@@ -35,7 +36,8 @@ type UsageClientProps = {
|
|
|
35
36
|
*/
|
|
36
37
|
export function UsageClient(props: UsageClientProps) {
|
|
37
38
|
const { formatText } = useAgentNaming();
|
|
38
|
-
const
|
|
39
|
+
const { language } = useServerLanguage();
|
|
40
|
+
const usageClientState = useUsageClientState(props, language);
|
|
39
41
|
|
|
40
42
|
return (
|
|
41
43
|
<div className="container mx-auto px-4 py-8 space-y-6">
|
|
@@ -86,7 +88,7 @@ export function UsageClient(props: UsageClientProps) {
|
|
|
86
88
|
) : null}
|
|
87
89
|
|
|
88
90
|
{!usageClientState.loading && usageClientState.data && (
|
|
89
|
-
<UsageClientAnalyticsPanels data={usageClientState.data} metric={usageClientState.metric} />
|
|
91
|
+
<UsageClientAnalyticsPanels data={usageClientState.data} language={language} metric={usageClientState.metric} />
|
|
90
92
|
)}
|
|
91
93
|
</div>
|
|
92
94
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Card } from '@/src/components/Homepage/Card';
|
|
2
|
+
import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
|
|
2
3
|
import type { UsageAnalyticsResponse, UsageMetricMode } from '@/src/utils/usageAdmin';
|
|
3
4
|
import type { ReactNode } from 'react';
|
|
4
5
|
import { useMemo } from 'react';
|
|
@@ -10,6 +11,7 @@ import { UsageClientTimelineChart } from './UsageClientTimelineChart';
|
|
|
10
11
|
*/
|
|
11
12
|
type UsageClientAnalyticsPanelsProps = {
|
|
12
13
|
data: UsageAnalyticsResponse;
|
|
14
|
+
language: ServerLanguageCode;
|
|
13
15
|
metric: UsageMetricMode;
|
|
14
16
|
};
|
|
15
17
|
|
|
@@ -19,7 +21,7 @@ type UsageClientAnalyticsPanelsProps = {
|
|
|
19
21
|
* @private function of UsageClient
|
|
20
22
|
*/
|
|
21
23
|
export function UsageClientAnalyticsPanels(props: UsageClientAnalyticsPanelsProps) {
|
|
22
|
-
const { data, metric } = props;
|
|
24
|
+
const { data, language, metric } = props;
|
|
23
25
|
|
|
24
26
|
const summaryItems = useMemo(() => {
|
|
25
27
|
const primaryMetricLabel = UsageClientFormatting.usageMetricLabel(metric);
|
|
@@ -62,7 +64,7 @@ export function UsageClientAnalyticsPanels(props: UsageClientAnalyticsPanelsProp
|
|
|
62
64
|
{UsageClientFormatting.usageMetricDescription(metric)} in the selected timeframe.
|
|
63
65
|
</p>
|
|
64
66
|
</div>
|
|
65
|
-
<UsageClientTimelineChart points={data.timeline} metric={metric} />
|
|
67
|
+
<UsageClientTimelineChart language={language} points={data.timeline} metric={metric} />
|
|
66
68
|
</Card>
|
|
67
69
|
|
|
68
70
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
|
@@ -164,7 +166,7 @@ export function UsageClientAnalyticsPanels(props: UsageClientAnalyticsPanelsProp
|
|
|
164
166
|
metric,
|
|
165
167
|
UsageClientFormatting.resolveMetricValue(item, metric),
|
|
166
168
|
),
|
|
167
|
-
UsageClientFormatting.formatDateTime(item.lastSeen),
|
|
169
|
+
UsageClientFormatting.formatDateTime(item.lastSeen, language),
|
|
168
170
|
])}
|
|
169
171
|
/>
|
|
170
172
|
</Card>
|
|
@@ -182,7 +184,7 @@ export function UsageClientAnalyticsPanels(props: UsageClientAnalyticsPanelsProp
|
|
|
182
184
|
metric,
|
|
183
185
|
UsageClientFormatting.resolveMetricValue(item, metric),
|
|
184
186
|
),
|
|
185
|
-
UsageClientFormatting.formatDateTime(item.lastSeen),
|
|
187
|
+
UsageClientFormatting.formatDateTime(item.lastSeen, language),
|
|
186
188
|
])}
|
|
187
189
|
/>
|
|
188
190
|
</Card>
|