@promptbook/cli 0.112.0-131 → 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.
Files changed (100) hide show
  1. package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +4 -1
  2. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +3 -0
  3. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackTable.tsx +13 -8
  4. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +3 -0
  5. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryTable.tsx +13 -8
  6. package/apps/agents-server/src/app/admin/custom-css/CustomCssClient.tsx +4 -0
  7. package/apps/agents-server/src/app/admin/custom-css/CustomCssEditorPanel.tsx +7 -1
  8. package/apps/agents-server/src/app/admin/custom-css/CustomCssFilesPanel.tsx +7 -1
  9. package/apps/agents-server/src/app/admin/custom-js/CustomJsClient.tsx +11 -0
  10. package/apps/agents-server/src/app/admin/custom-js/CustomJsEditorPanel.tsx +7 -1
  11. package/apps/agents-server/src/app/admin/custom-js/CustomJsFilesPanel.tsx +7 -1
  12. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +4 -0
  13. package/apps/agents-server/src/app/admin/files/FilesGalleryGrid.tsx +9 -11
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryTable.tsx +9 -8
  15. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +4 -0
  16. package/apps/agents-server/src/app/admin/images/ImagesGalleryGrid.tsx +16 -11
  17. package/apps/agents-server/src/app/admin/images/ImagesGalleryTable.tsx +18 -9
  18. package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +9 -9
  19. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +7 -6
  20. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +11 -10
  21. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +4 -2
  22. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +14 -15
  23. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +4 -1
  24. package/apps/agents-server/src/app/admin/task-manager/useTaskManagerState.ts +6 -9
  25. package/apps/agents-server/src/app/admin/update/CustomCommitPicker.tsx +258 -0
  26. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +305 -108
  27. package/apps/agents-server/src/app/admin/usage/UsageClient.tsx +4 -2
  28. package/apps/agents-server/src/app/admin/usage/UsageClientAnalyticsPanels.tsx +6 -4
  29. package/apps/agents-server/src/app/admin/usage/UsageClientFormatting.ts +6 -12
  30. package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +6 -3
  31. package/apps/agents-server/src/app/admin/usage/useUsageClientState.ts +12 -4
  32. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +8 -3
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +6 -20
  34. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorHistory.ts +9 -2
  35. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistoryClientState.ts +2 -15
  36. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +6 -1
  37. package/apps/agents-server/src/app/agents/[agentName]/integration/CalendarIntegrationSection.tsx +23 -5
  38. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +4 -2
  39. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +9 -4
  40. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +4 -1
  41. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +10 -3
  42. package/apps/agents-server/src/app/api/admin/update/commits/route.ts +35 -0
  43. package/apps/agents-server/src/app/api/admin/update/route.ts +10 -2
  44. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +14 -2
  45. package/apps/agents-server/src/app/dashboard/page.tsx +11 -16
  46. package/apps/agents-server/src/app/layout.tsx +12 -3
  47. package/apps/agents-server/src/app/swagger/SwaggerApiKeysPanel.tsx +4 -1
  48. package/apps/agents-server/src/app/system/user-memory/UserMemoryClient.tsx +4 -2
  49. package/apps/agents-server/src/app/system/user-wallet/UserWalletClient.tsx +3 -0
  50. package/apps/agents-server/src/app/system/user-wallet/UserWalletRecordsTable.tsx +5 -1
  51. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorClient.tsx +3 -0
  52. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorSidebar.tsx +7 -8
  53. package/apps/agents-server/src/components/Footer/Footer.tsx +3 -2
  54. package/apps/agents-server/src/components/Footer/getCommitFooterEmoji.ts +112 -0
  55. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -2
  56. package/apps/agents-server/src/components/UsersList/UsersList.tsx +4 -2
  57. package/apps/agents-server/src/languages/translations/czech.yaml +1 -1
  58. package/apps/agents-server/src/languages/translations/english.yaml +1 -1
  59. package/apps/agents-server/src/middleware/applyEmbeddingHeader.ts +4 -2
  60. package/apps/agents-server/src/middleware/contentSecurityPolicy.ts +97 -0
  61. package/apps/agents-server/src/tools/createAgentProgressTools.ts +8 -6
  62. package/apps/agents-server/src/utils/localization/formatServerLanguageHumanReadableDate.ts +70 -0
  63. package/apps/agents-server/src/utils/localization/getRequestServerLanguage.ts +32 -0
  64. package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +17 -0
  65. package/apps/agents-server/src/utils/userChat/createRunUserChatJobPersistenceController.ts +0 -21
  66. package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +31 -0
  67. package/apps/agents-server/src/utils/userChat/retryUserChatJob.ts +0 -4
  68. package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +29 -0
  69. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +2 -2
  70. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +99 -401
  71. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +625 -51
  72. package/esm/index.es.js +252 -41
  73. package/esm/index.es.js.map +1 -1
  74. package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  75. package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  76. package/esm/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  77. package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  78. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  79. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  80. package/esm/src/book-3.0/cliAgentEnv.d.ts +1 -1
  81. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  82. package/esm/src/version.d.ts +1 -1
  83. package/package.json +1 -1
  84. package/src/book-3.0/cliAgentEnv.ts +1 -1
  85. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +1 -1
  86. package/src/cli/cli-commands/coder/ThinkingLevel.ts +3 -1
  87. package/src/other/templates/getTemplatesPipelineCollection.ts +681 -766
  88. package/src/version.ts +2 -2
  89. package/src/versions.txt +2 -0
  90. package/umd/index.umd.js +254 -44
  91. package/umd/index.umd.js.map +1 -1
  92. package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  93. package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  94. package/umd/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  95. package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  96. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  97. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  98. package/umd/src/book-3.0/cliAgentEnv.d.ts +1 -1
  99. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  100. package/umd/src/version.d.ts +1 -1
@@ -1,6 +1,8 @@
1
1
  import { ChevronLeft, ChevronRight } from 'lucide-react';
2
2
  import Link from 'next/link';
3
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
4
  import { buildAgentProfileHref } from '../../../utils/agentRouting/agentRouteHrefs';
5
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import type { ImageWithAgent } from './actions';
5
7
  import { ImagesGalleryParametersBadges } from './ImagesGalleryParametersBadges';
6
8
  import { ImagesGalleryPromptCopyButton } from './ImagesGalleryPromptCopyButton';
@@ -18,6 +20,11 @@ type ImagesGalleryTableProps = Pick<
18
20
  * Active text formatter for agent naming.
19
21
  */
20
22
  readonly formatText: (text: string) => string;
23
+
24
+ /**
25
+ * Active UI language used for date formatting.
26
+ */
27
+ readonly language: ServerLanguageCode;
21
28
  };
22
29
 
23
30
  /**
@@ -28,19 +35,17 @@ type ImagesGalleryTableRowProps = Pick<UseImagesGalleryState, 'copiedId' | 'hand
28
35
  * Image rendered in this table row.
29
36
  */
30
37
  readonly image: ImageWithAgent;
31
- };
32
38
 
33
- /**
34
- * Formats one created-at value for the table view.
35
- */
36
- function formatImagesGalleryCreatedAt(createdAt: string): string {
37
- return new Date(createdAt).toLocaleString();
38
- }
39
+ /**
40
+ * Active UI language used for date formatting.
41
+ */
42
+ readonly language: ServerLanguageCode;
43
+ };
39
44
 
40
45
  /**
41
46
  * Renders one image row inside the gallery table.
42
47
  */
43
- function ImagesGalleryTableRow({ image, copiedId, handlePromptCopy }: ImagesGalleryTableRowProps) {
48
+ function ImagesGalleryTableRow({ image, language, copiedId, handlePromptCopy }: ImagesGalleryTableRowProps) {
44
49
  return (
45
50
  <tr className="bg-white border-b hover:bg-gray-50">
46
51
  <td className="px-6 py-4">
@@ -94,7 +99,9 @@ function ImagesGalleryTableRow({ image, copiedId, handlePromptCopy }: ImagesGall
94
99
  <span className="text-gray-400">-</span>
95
100
  )}
96
101
  </td>
97
- <td className="px-6 py-4 whitespace-nowrap">{formatImagesGalleryCreatedAt(image.createdAt)}</td>
102
+ <td className="px-6 py-4 whitespace-nowrap">
103
+ {formatServerLanguageHumanReadableDate(image.createdAt, language)}
104
+ </td>
98
105
  </tr>
99
106
  );
100
107
  }
@@ -106,6 +113,7 @@ function ImagesGalleryTableRow({ image, copiedId, handlePromptCopy }: ImagesGall
106
113
  */
107
114
  export function ImagesGalleryTable({
108
115
  formatText,
116
+ language,
109
117
  images,
110
118
  total,
111
119
  isLoading,
@@ -137,6 +145,7 @@ export function ImagesGalleryTable({
137
145
  <ImagesGalleryTableRow
138
146
  key={image.id}
139
147
  image={image}
148
+ language={language}
140
149
  copiedId={copiedId}
141
150
  handlePromptCopy={handlePromptCopy}
142
151
  />
@@ -5,6 +5,9 @@ import { $getTableName } from '../../../../database/$getTableName';
5
5
  import { $provideSupabaseForServer } from '../../../../database/$provideSupabaseForServer';
6
6
  import { ForbiddenPage } from '../../../../components/ForbiddenPage/ForbiddenPage';
7
7
  import { isUserAdmin } from '../../../../utils/isUserAdmin';
8
+ import type { ServerLanguageCode } from '../../../../languages/ServerLanguageRegistry';
9
+ import { formatServerLanguageHumanReadableDate } from '../../../../utils/localization/formatServerLanguageHumanReadableDate';
10
+ import { getRequestServerLanguage } from '../../../../utils/localization/getRequestServerLanguage';
8
11
  import {
9
12
  getShibbolethAuthenticationAttemptTableName,
10
13
  getShibbolethUserIdentityTableName,
@@ -112,12 +115,8 @@ async function loadShibbolethIdentitiesWithUsers(): Promise<{
112
115
  /**
113
116
  * Formats an optional date-time value for the dashboard.
114
117
  */
115
- function formatDashboardDateTime(value: string | null | undefined): string {
116
- if (!value) {
117
- return 'Never';
118
- }
119
-
120
- return new Date(value).toLocaleString();
118
+ function formatDashboardDateTime(value: string | null | undefined, language: ServerLanguageCode): string {
119
+ return formatServerLanguageHumanReadableDate(value, language, { fallbackLabel: 'Never' });
121
120
  }
122
121
 
123
122
  /**
@@ -131,13 +130,14 @@ export default async function ShibbolethLoginMethodPage() {
131
130
  }
132
131
 
133
132
  const requestUrl = await getDashboardRequestUrl();
134
- const [configuration, attempts, identitiesWithUsers] = await Promise.all([
133
+ const [configuration, attempts, identitiesWithUsers, language] = await Promise.all([
135
134
  resolveShibbolethAuthenticationConfiguration({
136
135
  requestUrl,
137
136
  isIdentityProviderMetadataValidationEnabled: true,
138
137
  }),
139
138
  loadShibbolethAuthenticationAttempts(),
140
139
  loadShibbolethIdentitiesWithUsers(),
140
+ getRequestServerLanguage(),
141
141
  ]);
142
142
  const isWarningShown = configuration.isActive && !configuration.isConfigured;
143
143
 
@@ -286,7 +286,7 @@ export default async function ShibbolethLoginMethodPage() {
286
286
  attempts.map((attempt) => (
287
287
  <tr key={attempt.id}>
288
288
  <td className="whitespace-nowrap py-2 pr-4 text-gray-700">
289
- {formatDashboardDateTime(attempt.createdAt)}
289
+ {formatDashboardDateTime(attempt.createdAt, language)}
290
290
  </td>
291
291
  <td className="py-2 pr-4 font-medium text-gray-900">{attempt.status}</td>
292
292
  <td className="py-2 pr-4 text-gray-700">{attempt.stage}</td>
@@ -350,7 +350,7 @@ export default async function ShibbolethLoginMethodPage() {
350
350
  </td>
351
351
  <td className="py-2 pr-4 text-gray-700">{identity.loginCount}</td>
352
352
  <td className="whitespace-nowrap py-2 pr-4 text-gray-700">
353
- {formatDashboardDateTime(identity.lastLoggedInAt)}
353
+ {formatDashboardDateTime(identity.lastLoggedInAt, language)}
354
354
  </td>
355
355
  </tr>
356
356
  );
@@ -1,21 +1,21 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useMemo, useState } from 'react';
4
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
4
5
  import { Card } from '../../../components/Homepage/Card';
6
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
5
7
  import {
6
8
  $fetchMessages,
7
9
  type MessageRow,
8
10
  type MessageSendAttemptRow,
9
11
  } from '../../../utils/messagesAdmin';
12
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
10
13
 
11
14
  /**
12
15
  * Formats date.
13
16
  */
14
- function formatDate(dateString: string | null | undefined): string {
15
- if (!dateString) return '-';
16
- const date = new Date(dateString);
17
- if (Number.isNaN(date.getTime())) return dateString;
18
- return date.toLocaleString();
17
+ function formatDate(dateString: string | null | undefined, language: ServerLanguageCode): string {
18
+ return formatServerLanguageHumanReadableDate(dateString, language, { fallbackLabel: '-' });
19
19
  }
20
20
 
21
21
  /**
@@ -44,6 +44,7 @@ function getStatusBadge(attempts: MessageSendAttemptRow[] | undefined) {
44
44
  * Handles messages client.
45
45
  */
46
46
  export function MessagesClient() {
47
+ const { language } = useServerLanguage();
47
48
  const [items, setItems] = useState<MessageRow[]>([]);
48
49
  const [total, setTotal] = useState(0);
49
50
  const [page, setPage] = useState(1);
@@ -270,7 +271,7 @@ export function MessagesClient() {
270
271
  {items.map((row) => (
271
272
  <tr key={row.id}>
272
273
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">
273
- {formatDate(row.createdAt)}
274
+ {formatDate(row.createdAt, language)}
274
275
  </td>
275
276
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">
276
277
  {row.channel}
@@ -2,7 +2,9 @@
2
2
 
3
3
  import { Fragment } from 'react';
4
4
  import { ArrowRightLeft, Loader2, RefreshCcw, Save } from 'lucide-react';
5
- import moment from 'moment';
5
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
6
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
7
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
6
8
  import {
7
9
  MANAGED_SERVER_ENVIRONMENT_OPTIONS,
8
10
  type ManagedServerEnvironment,
@@ -130,6 +132,7 @@ type ServersRegistryTableRowProps = {
130
132
  readonly onSaveServer: (serverId: number) => Promise<void>;
131
133
  readonly onSwitchServer: (server: ManagedServerRow) => Promise<void>;
132
134
  readonly onUpdateServerDraft: UpdateServerDraft;
135
+ readonly language: ServerLanguageCode;
133
136
  readonly server: ManagedServerRow;
134
137
  };
135
138
 
@@ -139,13 +142,8 @@ type ServersRegistryTableRowProps = {
139
142
  * @param value - ISO timestamp.
140
143
  * @returns Human-readable local timestamp.
141
144
  */
142
- function formatDateTime(value: string): string {
143
- if (!value) {
144
- return 'N/A';
145
- }
146
-
147
- const timestamp = moment(value);
148
- return timestamp.isValid() ? timestamp.format('YYYY-MM-DD HH:mm:ss') : 'N/A';
145
+ function formatDateTime(value: string, language: ServerLanguageCode): string {
146
+ return formatServerLanguageHumanReadableDate(value, language);
149
147
  }
150
148
 
151
149
  /**
@@ -187,6 +185,7 @@ function ServersRegistryTableRow(props: ServersRegistryTableRowProps) {
187
185
  onSaveServer,
188
186
  onSwitchServer,
189
187
  onUpdateServerDraft,
188
+ language,
190
189
  server,
191
190
  } = props;
192
191
  const isCurrent = server.id === currentServerId;
@@ -264,10 +263,10 @@ function ServersRegistryTableRow(props: ServersRegistryTableRowProps) {
264
263
  </div>
265
264
  </td>
266
265
  <td className="px-4 py-3 align-top text-xs text-gray-600">
267
- <span className="whitespace-nowrap font-mono">{formatDateTime(server.createdAt)}</span>
266
+ <span className="whitespace-nowrap font-mono">{formatDateTime(server.createdAt, language)}</span>
268
267
  </td>
269
268
  <td className="px-4 py-3 align-top text-xs text-gray-600">
270
- <span className="whitespace-nowrap font-mono">{formatDateTime(server.updatedAt)}</span>
269
+ <span className="whitespace-nowrap font-mono">{formatDateTime(server.updatedAt, language)}</span>
271
270
  </td>
272
271
  <td className="px-4 py-3 align-top">
273
272
  <div className="flex flex-wrap justify-end gap-2">
@@ -409,6 +408,7 @@ function ServersRegistryTableRow(props: ServersRegistryTableRowProps) {
409
408
  * @private helper component of <ServersClient/>
410
409
  */
411
410
  export function ServersRegistryTable(props: ServersRegistryTableProps) {
411
+ const { language } = useServerLanguage();
412
412
  const {
413
413
  currentServerId,
414
414
  canEdit,
@@ -495,6 +495,7 @@ export function ServersRegistryTable(props: ServersRegistryTableProps) {
495
495
  onSaveServer={onSaveServer}
496
496
  onSwitchServer={onSwitchServer}
497
497
  onUpdateServerDraft={onUpdateServerDraft}
498
+ language={language}
498
499
  server={server}
499
500
  />
500
501
  </Fragment>
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Card } from '../../../components/Homepage/Card';
4
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
4
5
  import { TaskManagerFiltersCard } from './TaskManagerFiltersCard';
5
6
  import { TaskManagerSummaryMetrics } from './TaskManagerSummaryMetrics';
6
7
  import { TaskManagerTasksCard } from './TaskManagerTasksCard';
@@ -12,7 +13,8 @@ import { useTaskManagerState } from './useTaskManagerState';
12
13
  * @private route component of AdminTaskManagerPage
13
14
  */
14
15
  export function TaskManagerClient() {
15
- const taskManagerState = useTaskManagerState();
16
+ const { language } = useServerLanguage();
17
+ const taskManagerState = useTaskManagerState(language);
16
18
 
17
19
  return (
18
20
  <div className="container mx-auto space-y-6 px-4 py-8">
@@ -53,7 +55,7 @@ export function TaskManagerClient() {
53
55
  </Card>
54
56
  ) : null}
55
57
 
56
- <TaskManagerTasksCard state={taskManagerState} />
58
+ <TaskManagerTasksCard language={language} state={taskManagerState} />
57
59
  </div>
58
60
  );
59
61
  }
@@ -1,4 +1,6 @@
1
1
  import type { AdminChatTaskRecord } from '@/src/utils/chatTasksAdmin';
2
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
3
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
2
4
  import type { useTaskManagerState } from './useTaskManagerState';
3
5
 
4
6
  /**
@@ -9,6 +11,7 @@ import type { useTaskManagerState } from './useTaskManagerState';
9
11
  type TaskManagerTaskRowProps = {
10
12
  busyAction: ReturnType<typeof useTaskManagerState>['busyAction'];
11
13
  busyTaskId: ReturnType<typeof useTaskManagerState>['busyTaskId'];
14
+ language: ServerLanguageCode;
12
15
  onRunTaskAction: ReturnType<typeof useTaskManagerState>['runTaskAction'];
13
16
  stuckThresholdMinutes: ReturnType<typeof useTaskManagerState>['stuckThresholdMinutes'];
14
17
  task: AdminChatTaskRecord;
@@ -135,13 +138,8 @@ function formatTaskKind(kind: AdminChatTaskRecord['kind']): string {
135
138
  *
136
139
  * @private function of TaskManagerTaskRow
137
140
  */
138
- function formatDateTime(value: string | null): string {
139
- if (!value) {
140
- return '-';
141
- }
142
-
143
- const parsed = new Date(value);
144
- return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString();
141
+ function formatDateTime(value: string | null, language: ServerLanguageCode): string {
142
+ return formatServerLanguageHumanReadableDate(value, language, { fallbackLabel: '-' });
145
143
  }
146
144
 
147
145
  /**
@@ -306,6 +304,7 @@ function TaskManagerTaskActions({ busyAction, isBusy, onRunTaskAction, task }: T
306
304
  export function TaskManagerTaskRow({
307
305
  busyAction,
308
306
  busyTaskId,
307
+ language,
309
308
  onRunTaskAction,
310
309
  stuckThresholdMinutes,
311
310
  task,
@@ -341,7 +340,7 @@ export function TaskManagerTaskRow({
341
340
  <div className="font-medium text-orange-700">Cancellation requested</div>
342
341
  ) : null}
343
342
  {task.pausedAt ? (
344
- <div className="font-medium text-orange-700">Paused {formatDateTime(task.pausedAt)}</div>
343
+ <div className="font-medium text-orange-700">Paused {formatDateTime(task.pausedAt, language)}</div>
345
344
  ) : null}
346
345
  </div>
347
346
  </td>
@@ -367,10 +366,10 @@ export function TaskManagerTaskRow({
367
366
  <td className="px-4 py-3 align-top">
368
367
  <TaskInfoBlock
369
368
  rows={[
370
- { label: 'Created', value: formatDateTime(task.createdAt) },
371
- { label: 'Started', value: formatDateTime(task.startedAt) },
372
- { label: 'Updated', value: formatDateTime(task.updatedAt) },
373
- { label: 'Finished', value: formatDateTime(task.finishedAt) },
369
+ { label: 'Created', value: formatDateTime(task.createdAt, language) },
370
+ { label: 'Started', value: formatDateTime(task.startedAt, language) },
371
+ { label: 'Updated', value: formatDateTime(task.updatedAt, language) },
372
+ { label: 'Finished', value: formatDateTime(task.finishedAt, language) },
374
373
  ]}
375
374
  />
376
375
  </td>
@@ -381,7 +380,7 @@ export function TaskManagerTaskRow({
381
380
  { label: 'Queue age', value: formatDuration(getQueueAgeMs(task)) },
382
381
  { label: 'Runtime', value: formatDuration(getRuntimeDurationMs(task)) },
383
382
  { label: 'Total', value: formatDuration(getTotalDurationMs(task)) },
384
- { label: 'Heartbeat', value: formatDateTime(task.lastHeartbeatAt) },
383
+ { label: 'Heartbeat', value: formatDateTime(task.lastHeartbeatAt, language) },
385
384
  ]}
386
385
  />
387
386
  </td>
@@ -391,8 +390,8 @@ export function TaskManagerTaskRow({
391
390
  rows={[
392
391
  { label: 'Queue', value: task.queueName || '-' },
393
392
  { label: 'Worker', value: task.workerId || '-' },
394
- { label: 'Lease expires', value: formatDateTime(task.leaseExpiresAt) },
395
- { label: 'Paused at', value: formatDateTime(task.pausedAt) },
393
+ { label: 'Lease expires', value: formatDateTime(task.leaseExpiresAt, language) },
394
+ { label: 'Paused at', value: formatDateTime(task.pausedAt, language) },
396
395
  ]}
397
396
  />
398
397
  </td>
@@ -1,3 +1,4 @@
1
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
1
2
  import type { useTaskManagerState } from './useTaskManagerState';
2
3
  import { Card } from '../../../components/Homepage/Card';
3
4
  import { TaskManagerTaskRow } from './TaskManagerTaskRow';
@@ -8,6 +9,7 @@ import { TaskManagerTaskRow } from './TaskManagerTaskRow';
8
9
  * @private function of TaskManagerClient
9
10
  */
10
11
  type TaskManagerTasksCardProps = {
12
+ language: ServerLanguageCode;
11
13
  state: ReturnType<typeof useTaskManagerState>;
12
14
  };
13
15
 
@@ -41,7 +43,7 @@ function resolveEmptyStateMessage(view: ReturnType<typeof useTaskManagerState>['
41
43
  *
42
44
  * @private function of TaskManagerClient
43
45
  */
44
- export function TaskManagerTasksCard({ state }: TaskManagerTasksCardProps) {
46
+ export function TaskManagerTasksCard({ language, state }: TaskManagerTasksCardProps) {
45
47
  const firstVisibleTaskIndex = Math.min((state.page - 1) * state.pageSize + 1, state.total);
46
48
  const lastVisibleTaskIndex = Math.min(state.page * state.pageSize, state.total);
47
49
 
@@ -82,6 +84,7 @@ export function TaskManagerTasksCard({ state }: TaskManagerTasksCardProps) {
82
84
  <TaskManagerTaskRow
83
85
  key={task.id}
84
86
  task={task}
87
+ language={language}
85
88
  busyAction={state.busyAction}
86
89
  busyTaskId={state.busyTaskId}
87
90
  onRunTaskAction={state.runTaskAction}
@@ -9,6 +9,8 @@ import {
9
9
  type AdminChatTaskRecord,
10
10
  type AdminChatTaskView,
11
11
  } from '@/src/utils/chatTasksAdmin';
12
+ import type { ServerLanguageCode } from '@/src/languages/ServerLanguageRegistry';
13
+ import { formatServerLanguageHumanReadableDate } from '@/src/utils/localization/formatServerLanguageHumanReadableDate';
12
14
  import { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react';
13
15
 
14
16
  /**
@@ -86,13 +88,8 @@ function parseSelectedNumber(value: string, fallback: number): number {
86
88
  *
87
89
  * @private function of TaskManagerClient
88
90
  */
89
- function formatDateTime(value: string | null): string {
90
- if (!value) {
91
- return '-';
92
- }
93
-
94
- const parsed = new Date(value);
95
- return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString();
91
+ function formatDateTime(value: string | null, language: ServerLanguageCode): string {
92
+ return formatServerLanguageHumanReadableDate(value, language, { fallbackLabel: '-' });
96
93
  }
97
94
 
98
95
  /**
@@ -244,7 +241,7 @@ function resolveTaskLoadErrorMessage(loadError: unknown): string {
244
241
  *
245
242
  * @private function of TaskManagerClient
246
243
  */
247
- export function useTaskManagerState(): UseTaskManagerStateResult {
244
+ export function useTaskManagerState(language: ServerLanguageCode): UseTaskManagerStateResult {
248
245
  const [view, setView] = useState<AdminChatTaskView>('active');
249
246
  const [page, setPage] = useState(1);
250
247
  const [pageSize, setPageSize] = useState(50);
@@ -405,7 +402,7 @@ export function useTaskManagerState(): UseTaskManagerStateResult {
405
402
  isPreviousPageDisabled: page <= 1,
406
403
  isRefreshing,
407
404
  lastRefreshedLabel: generatedAt
408
- ? `Last refreshed ${formatDateTime(generatedAt)}`
405
+ ? `Last refreshed ${formatDateTime(generatedAt, language)}`
409
406
  : 'Waiting for first refresh…',
410
407
  oldestQueuedAgeLabel: counters ? formatDuration(counters.oldestQueuedAgeMs) : '...',
411
408
  page,