@promptbook/cli 0.113.0-10 → 0.113.0-11
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/{code-runners/CodeRunnersClient.tsx → harness-auth/HarnessAuthClient.tsx} +98 -65
- package/apps/agents-server/src/app/admin/{code-runners → harness-auth}/page.tsx +4 -4
- package/apps/agents-server/src/app/api/admin/{code-runners → harness-auth}/authentication/route.ts +17 -17
- package/apps/agents-server/src/app/api/admin/{code-runners → harness-auth}/route.ts +13 -13
- package/apps/agents-server/src/app/layout.tsx +16 -0
- package/apps/agents-server/src/components/AdminTerminal/AdminTerminalCard.tsx +32 -24
- package/apps/agents-server/src/components/Footer/Footer.tsx +38 -15
- package/apps/agents-server/src/components/Header/buildHeaderSystemMenuItems.ts +5 -4
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +4 -1
- package/apps/agents-server/src/constants/harnessAuthRoutes.ts +20 -0
- package/apps/agents-server/src/languages/ServerTranslationKeys.ts +1 -1
- package/apps/agents-server/src/languages/translations/czech.yaml +2 -2
- package/apps/agents-server/src/languages/translations/english.yaml +1 -1
- package/apps/agents-server/src/utils/{codeRunnerAuthentication.ts → harnessAuthentication.ts} +72 -56
- package/apps/agents-server/src/utils/{codeRunnerConfiguration.ts → harnessConfiguration.ts} +18 -12
- package/apps/agents-server/src/utils/taskTerminal/resolveAdminTaskTerminalSession.ts +28 -5
- package/apps/agents-server/src/utils/vpsConfiguration.ts +3 -3
- package/apps/agents-server/src/utils/vpsSelfUpdate/readAgentsServerFooterVersion.ts +335 -0
- package/apps/agents-server/src/utils/vpsSelfUpdate/vpsSelfUpdateJobHistory.ts +107 -4
- package/esm/index.es.js +697 -591
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/sleepWithCountdown.d.ts +1 -0
- package/esm/scripts/run-codex-prompts/common/waitUntilWorldTimeDeadline.d.ts +17 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +3 -16
- package/esm/src/utils/agents/terminalAgentAvatarVisual.d.ts +94 -0
- package/esm/src/utils/agents/terminalAgentAvatarVisual.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/other/templates/getTemplatesPipelineCollection.ts +790 -807
- package/src/utils/agents/terminalAgentAvatarVisual.ts +261 -0
- package/src/version.ts +2 -2
- package/src/versions.txt +1 -0
- package/umd/index.umd.js +697 -591
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/sleepWithCountdown.d.ts +1 -0
- package/umd/scripts/run-codex-prompts/common/waitUntilWorldTimeDeadline.d.ts +17 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunAgentVisual.d.ts +3 -16
- package/umd/src/utils/agents/terminalAgentAvatarVisual.d.ts +94 -0
- package/umd/src/utils/agents/terminalAgentAvatarVisual.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
import { Loader2, Save, ServerCog } from 'lucide-react';
|
|
4
4
|
import { useCallback, useEffect, useState } from 'react';
|
|
5
5
|
import { AdminTerminalCard } from '../../../components/AdminTerminal/AdminTerminalCard';
|
|
6
|
-
import { AdminXtermTerminal } from '../../../components/AdminTerminal/AdminXtermTerminal';
|
|
7
6
|
import { useAdminTerminalSession } from '../../../components/AdminTerminal/useAdminTerminalSession';
|
|
8
7
|
import { Card } from '../../../components/Homepage/Card';
|
|
8
|
+
import { HARNESS_AUTH_API_PATH, HARNESS_AUTHENTICATION_API_PATH } from '../../../constants/harnessAuthRoutes';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Harness Auth API response.
|
|
12
12
|
*/
|
|
13
|
-
type
|
|
14
|
-
readonly
|
|
13
|
+
type HarnessAuthResponse = {
|
|
14
|
+
readonly harness?: string;
|
|
15
15
|
readonly model?: string;
|
|
16
16
|
readonly thinkingLevel?: string;
|
|
17
17
|
readonly status?: string;
|
|
@@ -25,9 +25,9 @@ type CodeRunnersResponse = {
|
|
|
25
25
|
/**
|
|
26
26
|
* Browser-safe snapshot of one authentication terminal session.
|
|
27
27
|
*/
|
|
28
|
-
type
|
|
28
|
+
type HarnessAuthenticationSession = {
|
|
29
29
|
readonly id: string;
|
|
30
|
-
readonly
|
|
30
|
+
readonly harness: string;
|
|
31
31
|
readonly isRunning: boolean;
|
|
32
32
|
readonly output: string;
|
|
33
33
|
readonly startedAt: string;
|
|
@@ -37,9 +37,9 @@ type CodeRunnerAuthenticationSession = {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Supported
|
|
40
|
+
* Supported harness options shown by the standalone UI.
|
|
41
41
|
*/
|
|
42
|
-
const
|
|
42
|
+
const HARNESS_OPTIONS = [
|
|
43
43
|
{ value: 'github-copilot', label: 'GitHub Copilot' },
|
|
44
44
|
{ value: 'openai-codex', label: 'OpenAI Codex' },
|
|
45
45
|
{ value: 'claude-code', label: 'Claude Code' },
|
|
@@ -48,7 +48,7 @@ const RUNNER_OPTIONS = [
|
|
|
48
48
|
] as const;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
* Contextual UI copy for the
|
|
51
|
+
* Contextual UI copy for the harness authentication terminal.
|
|
52
52
|
*/
|
|
53
53
|
const AUTHENTICATION_HINTS: Record<string, string> = {
|
|
54
54
|
'github-copilot':
|
|
@@ -60,20 +60,58 @@ const AUTHENTICATION_HINTS: Record<string, string> = {
|
|
|
60
60
|
opencode:
|
|
61
61
|
'Start the terminal and complete the Opencode authentication flow directly there, including any browser/device confirmation, then exit the CLI.',
|
|
62
62
|
gemini:
|
|
63
|
-
'Start the terminal and complete the Gemini CLI authentication prompts there, then exit the CLI after it confirms that the
|
|
63
|
+
'Start the terminal and complete the Gemini CLI authentication prompts there, then exit the CLI after it confirms that the harness is ready.',
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Shared input styling for
|
|
67
|
+
* Shared input styling for harness controls.
|
|
68
68
|
*/
|
|
69
69
|
const INPUT_CLASS_NAME =
|
|
70
70
|
'w-full rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 shadow-sm focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-200 disabled:bg-gray-50 disabled:text-gray-500';
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* Shared plain-output text styling for non-interactive harness command results.
|
|
74
74
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
const HARNESS_PLAIN_OUTPUT_TEXT_CLASS_NAME =
|
|
76
|
+
'whitespace-pre-wrap break-words font-mono text-xs leading-5 text-slate-600';
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Props for the non-terminal harness output block.
|
|
80
|
+
*/
|
|
81
|
+
type HarnessPlainOutputBlockProps = {
|
|
82
|
+
/**
|
|
83
|
+
* Visible output heading.
|
|
84
|
+
*/
|
|
85
|
+
readonly title: string;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Plain output text to show.
|
|
89
|
+
*/
|
|
90
|
+
readonly output: string;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Optional max-height and overflow classes for longer output.
|
|
94
|
+
*/
|
|
95
|
+
readonly outputSizeClassName?: string;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Plain text command output block used when an interactive terminal would be misleading.
|
|
100
|
+
*/
|
|
101
|
+
function HarnessPlainOutputBlock({ title, output, outputSizeClassName = '' }: HarnessPlainOutputBlockProps) {
|
|
102
|
+
return (
|
|
103
|
+
<div className="rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
|
|
104
|
+
<p className="text-sm font-semibold text-slate-700">{title}</p>
|
|
105
|
+
<pre className={`mt-2 ${outputSizeClassName} ${HARNESS_PLAIN_OUTPUT_TEXT_CLASS_NAME}`}>{output}</pre>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Client UI for configuring the local harness used by durable chats.
|
|
112
|
+
*/
|
|
113
|
+
export function HarnessAuthClient() {
|
|
114
|
+
const [harness, setHarness] = useState('github-copilot');
|
|
77
115
|
const [model, setModel] = useState('gpt-5.4');
|
|
78
116
|
const [thinkingLevel, setThinkingLevel] = useState('xhigh');
|
|
79
117
|
const [status, setStatus] = useState('');
|
|
@@ -82,15 +120,15 @@ export function CodeRunnersClient() {
|
|
|
82
120
|
const [configurationErrorMessage, setConfigurationErrorMessage] = useState<string | null>(null);
|
|
83
121
|
const [configurationSuccessMessage, setConfigurationSuccessMessage] = useState<string | null>(null);
|
|
84
122
|
const [applyOutput, setApplyOutput] = useState<string | null>(null);
|
|
85
|
-
const authenticationTerminal = useAdminTerminalSession<
|
|
86
|
-
basePath:
|
|
123
|
+
const authenticationTerminal = useAdminTerminalSession<HarnessAuthenticationSession>({
|
|
124
|
+
basePath: HARNESS_AUTHENTICATION_API_PATH,
|
|
87
125
|
loadErrorMessage: 'Failed to load the authentication session.',
|
|
88
126
|
startErrorMessage: 'Failed to start the authentication session.',
|
|
89
127
|
sendErrorMessage: 'Failed to send authentication input.',
|
|
90
128
|
stopErrorMessage: 'Failed to stop the authentication session.',
|
|
91
|
-
startSuccessMessage: '
|
|
92
|
-
finishSuccessMessage: '
|
|
93
|
-
finishErrorMessage: '
|
|
129
|
+
startSuccessMessage: 'Harness authentication terminal started.',
|
|
130
|
+
finishSuccessMessage: 'Harness authentication finished.',
|
|
131
|
+
finishErrorMessage: 'Harness authentication session ended with an error.',
|
|
94
132
|
});
|
|
95
133
|
const {
|
|
96
134
|
session: authenticationSession,
|
|
@@ -106,27 +144,27 @@ export function CodeRunnersClient() {
|
|
|
106
144
|
stopSession: stopAuthenticationSession,
|
|
107
145
|
} = authenticationTerminal;
|
|
108
146
|
const authenticationHint =
|
|
109
|
-
AUTHENTICATION_HINTS[
|
|
110
|
-
'Start the terminal, complete the
|
|
147
|
+
AUTHENTICATION_HINTS[harness] ||
|
|
148
|
+
'Start the terminal, complete the harness authentication flow there, and exit the CLI when the harness is ready.';
|
|
111
149
|
const errorMessage = configurationErrorMessage ?? authenticationErrorMessage;
|
|
112
150
|
const successMessage = configurationSuccessMessage ?? authenticationSuccessMessage;
|
|
113
151
|
|
|
114
152
|
/**
|
|
115
|
-
* Loads current
|
|
153
|
+
* Loads current harness settings.
|
|
116
154
|
*/
|
|
117
155
|
const loadConfiguration = useCallback(async (): Promise<void> => {
|
|
118
156
|
try {
|
|
119
157
|
setIsLoading(true);
|
|
120
158
|
setConfigurationErrorMessage(null);
|
|
121
159
|
|
|
122
|
-
const response = await fetch(
|
|
123
|
-
const payload = (await response.json()) as
|
|
160
|
+
const response = await fetch(HARNESS_AUTH_API_PATH, { cache: 'no-store' });
|
|
161
|
+
const payload = (await response.json()) as HarnessAuthResponse;
|
|
124
162
|
|
|
125
163
|
if (!response.ok) {
|
|
126
|
-
throw new Error(payload.error || 'Failed to load
|
|
164
|
+
throw new Error(payload.error || 'Failed to load harness configuration.');
|
|
127
165
|
}
|
|
128
166
|
|
|
129
|
-
|
|
167
|
+
setHarness(payload.harness || 'github-copilot');
|
|
130
168
|
setModel(payload.model || 'gpt-5.4');
|
|
131
169
|
setThinkingLevel(payload.thinkingLevel || 'xhigh');
|
|
132
170
|
setStatus(payload.status || '');
|
|
@@ -134,7 +172,7 @@ export function CodeRunnersClient() {
|
|
|
134
172
|
await loadAuthenticationSession();
|
|
135
173
|
} catch (error) {
|
|
136
174
|
setConfigurationErrorMessage(
|
|
137
|
-
error instanceof Error ? error.message : 'Failed to load
|
|
175
|
+
error instanceof Error ? error.message : 'Failed to load harness configuration.',
|
|
138
176
|
);
|
|
139
177
|
} finally {
|
|
140
178
|
setIsLoading(false);
|
|
@@ -154,7 +192,7 @@ export function CodeRunnersClient() {
|
|
|
154
192
|
}, [authenticationSession?.finishedAt, loadConfiguration]);
|
|
155
193
|
|
|
156
194
|
/**
|
|
157
|
-
* Saves
|
|
195
|
+
* Saves harness settings into `.env`.
|
|
158
196
|
*/
|
|
159
197
|
async function saveConfiguration(applyRuntimeConfiguration: boolean): Promise<void> {
|
|
160
198
|
try {
|
|
@@ -164,32 +202,32 @@ export function CodeRunnersClient() {
|
|
|
164
202
|
clearAuthenticationMessages();
|
|
165
203
|
setApplyOutput(null);
|
|
166
204
|
|
|
167
|
-
const response = await fetch(
|
|
205
|
+
const response = await fetch(HARNESS_AUTH_API_PATH, {
|
|
168
206
|
method: 'PATCH',
|
|
169
207
|
headers: {
|
|
170
208
|
'Content-Type': 'application/json',
|
|
171
209
|
},
|
|
172
|
-
body: JSON.stringify({
|
|
210
|
+
body: JSON.stringify({ harness, model, thinkingLevel, applyRuntimeConfiguration }),
|
|
173
211
|
});
|
|
174
|
-
const payload = (await response.json()) as
|
|
212
|
+
const payload = (await response.json()) as HarnessAuthResponse;
|
|
175
213
|
|
|
176
214
|
if (!response.ok) {
|
|
177
|
-
throw new Error(payload.error || 'Failed to save
|
|
215
|
+
throw new Error(payload.error || 'Failed to save harness configuration.');
|
|
178
216
|
}
|
|
179
217
|
|
|
180
|
-
|
|
218
|
+
setHarness(payload.harness || harness);
|
|
181
219
|
setModel(payload.model || model);
|
|
182
220
|
setThinkingLevel(payload.thinkingLevel || thinkingLevel);
|
|
183
221
|
setStatus(payload.status || '');
|
|
184
222
|
setApplyOutput(payload.applyResult?.output || null);
|
|
185
223
|
setConfigurationSuccessMessage(
|
|
186
224
|
applyRuntimeConfiguration
|
|
187
|
-
? '
|
|
188
|
-
: '
|
|
225
|
+
? 'Harness configuration was saved and applied.'
|
|
226
|
+
: 'Harness configuration was saved.',
|
|
189
227
|
);
|
|
190
228
|
} catch (error) {
|
|
191
229
|
setConfigurationErrorMessage(
|
|
192
|
-
error instanceof Error ? error.message : 'Failed to save
|
|
230
|
+
error instanceof Error ? error.message : 'Failed to save harness configuration.',
|
|
193
231
|
);
|
|
194
232
|
} finally {
|
|
195
233
|
setIsSaving(false);
|
|
@@ -199,9 +237,10 @@ export function CodeRunnersClient() {
|
|
|
199
237
|
return (
|
|
200
238
|
<div className="container mx-auto space-y-6 px-4 py-8">
|
|
201
239
|
<div className="mt-20">
|
|
202
|
-
<h1 className="text-3xl font-light text-gray-900">
|
|
240
|
+
<h1 className="text-3xl font-light text-gray-900">Harness Auth</h1>
|
|
203
241
|
<p className="mt-1 text-sm text-gray-500">
|
|
204
|
-
Configure the
|
|
242
|
+
Configure the harness used by the standalone Agents Server durable chat worker and sign in to
|
|
243
|
+
subscription-backed CLIs such as OpenAI Codex.
|
|
205
244
|
</p>
|
|
206
245
|
</div>
|
|
207
246
|
|
|
@@ -219,14 +258,14 @@ export function CodeRunnersClient() {
|
|
|
219
258
|
<Card className="hover:border-gray-200 hover:shadow-md">
|
|
220
259
|
<div className="grid gap-5 md:grid-cols-3">
|
|
221
260
|
<label className="space-y-2">
|
|
222
|
-
<span className="text-sm font-semibold text-slate-700">
|
|
261
|
+
<span className="text-sm font-semibold text-slate-700">Harness</span>
|
|
223
262
|
<select
|
|
224
|
-
value={
|
|
225
|
-
onChange={(event) =>
|
|
263
|
+
value={harness}
|
|
264
|
+
onChange={(event) => setHarness(event.target.value)}
|
|
226
265
|
disabled={isLoading || isSaving || isStartingAuthentication}
|
|
227
266
|
className={INPUT_CLASS_NAME}
|
|
228
267
|
>
|
|
229
|
-
{
|
|
268
|
+
{HARNESS_OPTIONS.map((option) => (
|
|
230
269
|
<option key={option.value} value={option.value}>
|
|
231
270
|
{option.label}
|
|
232
271
|
</option>
|
|
@@ -263,7 +302,7 @@ export function CodeRunnersClient() {
|
|
|
263
302
|
className="inline-flex items-center gap-2 rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60"
|
|
264
303
|
>
|
|
265
304
|
{isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
|
266
|
-
Save
|
|
305
|
+
Save harness
|
|
267
306
|
</button>
|
|
268
307
|
<button
|
|
269
308
|
type="button"
|
|
@@ -272,26 +311,29 @@ export function CodeRunnersClient() {
|
|
|
272
311
|
className="inline-flex items-center gap-2 rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50 disabled:cursor-not-allowed disabled:opacity-60"
|
|
273
312
|
>
|
|
274
313
|
{isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <ServerCog className="h-4 w-4" />}
|
|
275
|
-
Save and apply
|
|
314
|
+
Save and apply harness
|
|
276
315
|
</button>
|
|
277
316
|
</div>
|
|
317
|
+
|
|
318
|
+
<div className="mt-6">
|
|
319
|
+
<HarnessPlainOutputBlock
|
|
320
|
+
title="Harness status"
|
|
321
|
+
output={status || 'Harness status was not available.'}
|
|
322
|
+
/>
|
|
323
|
+
</div>
|
|
278
324
|
</Card>
|
|
279
325
|
|
|
280
326
|
{applyOutput ? (
|
|
281
|
-
<
|
|
282
|
-
|
|
327
|
+
<HarnessPlainOutputBlock
|
|
328
|
+
title="Harness apply output"
|
|
283
329
|
output={applyOutput}
|
|
284
|
-
|
|
285
|
-
isReadOnly
|
|
286
|
-
isPlainTextOutput
|
|
287
|
-
heightClassName="h-72"
|
|
288
|
-
ariaLabel="Code-runner apply output"
|
|
330
|
+
outputSizeClassName="max-h-72 overflow-auto"
|
|
289
331
|
/>
|
|
290
332
|
) : null}
|
|
291
333
|
|
|
292
334
|
<AdminTerminalCard
|
|
293
335
|
title="Authentication"
|
|
294
|
-
description="Save
|
|
336
|
+
description="Save harness changes first if you want to authenticate a different CLI, then start the saved-harness terminal here instead of SSHing into the VPS."
|
|
295
337
|
hint={authenticationHint}
|
|
296
338
|
session={authenticationSession}
|
|
297
339
|
onStart={() => void startAuthenticationSession()}
|
|
@@ -301,23 +343,14 @@ export function CodeRunnersClient() {
|
|
|
301
343
|
isStarting={isStartingAuthentication}
|
|
302
344
|
isSending={isSendingAuthenticationInput}
|
|
303
345
|
isStopping={isStoppingAuthentication}
|
|
304
|
-
startLabel="Authenticate saved
|
|
346
|
+
startLabel="Authenticate saved harness"
|
|
305
347
|
runningLabel="Authentication running"
|
|
306
348
|
stopLabel="Stop terminal"
|
|
307
349
|
outputLabel="Live authentication terminal"
|
|
308
|
-
outputEmptyState="No authentication session output yet. Start the saved-
|
|
350
|
+
outputEmptyState="No authentication session output yet. Start the saved-harness terminal to see the live authentication log here."
|
|
351
|
+
isOutputVisible={Boolean(authenticationSession?.isRunning)}
|
|
309
352
|
quickActions={[{ label: 'Send Enter', input: '\n' }]}
|
|
310
|
-
|
|
311
|
-
<AdminXtermTerminal
|
|
312
|
-
terminalId="code-runner-status"
|
|
313
|
-
output={status || 'Runner status was not available.'}
|
|
314
|
-
emptyState="Runner status was not available."
|
|
315
|
-
isReadOnly
|
|
316
|
-
isPlainTextOutput
|
|
317
|
-
heightClassName="h-64"
|
|
318
|
-
ariaLabel="Runner status"
|
|
319
|
-
/>
|
|
320
|
-
</AdminTerminalCard>
|
|
353
|
+
/>
|
|
321
354
|
</div>
|
|
322
355
|
);
|
|
323
356
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
|
|
2
2
|
import { isUserGlobalAdmin } from '../../../utils/isUserGlobalAdmin';
|
|
3
|
-
import {
|
|
3
|
+
import { HarnessAuthClient } from './HarnessAuthClient';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Super-admin page for configuring standalone
|
|
6
|
+
* Super-admin page for configuring standalone harness authentication.
|
|
7
7
|
*/
|
|
8
|
-
export default async function
|
|
8
|
+
export default async function HarnessAuthPage() {
|
|
9
9
|
if (!(await isUserGlobalAdmin())) {
|
|
10
10
|
return <ForbiddenPage />;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
return <
|
|
13
|
+
return <HarnessAuthClient />;
|
|
14
14
|
}
|
package/apps/agents-server/src/app/api/admin/{code-runners → harness-auth}/authentication/route.ts
RENAMED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from '@/src/utils/
|
|
2
|
+
getHarnessAuthenticationSession,
|
|
3
|
+
getLatestHarnessAuthenticationSession,
|
|
4
|
+
startHarnessAuthenticationSession,
|
|
5
|
+
stopHarnessAuthenticationSession,
|
|
6
|
+
subscribeToHarnessAuthenticationSession,
|
|
7
|
+
writeHarnessAuthenticationSessionInput,
|
|
8
|
+
} from '@/src/utils/harnessAuthentication';
|
|
9
9
|
import { createAdminTerminalRouteHandlers } from '@/src/utils/createAdminTerminalRouteHandlers';
|
|
10
|
-
import {
|
|
10
|
+
import { readConfiguredHarness } from '@/src/utils/harnessConfiguration';
|
|
11
11
|
|
|
12
12
|
export const runtime = 'nodejs';
|
|
13
13
|
export const dynamic = 'force-dynamic';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Shared route handlers for the
|
|
16
|
+
* Shared route handlers for the harness authentication terminal.
|
|
17
17
|
*/
|
|
18
18
|
const authenticationTerminalRouteHandlers = createAdminTerminalRouteHandlers(
|
|
19
19
|
{
|
|
20
20
|
async getLatestSession() {
|
|
21
|
-
const {
|
|
22
|
-
return
|
|
21
|
+
const { harness } = await readConfiguredHarness();
|
|
22
|
+
return getLatestHarnessAuthenticationSession(harness);
|
|
23
23
|
},
|
|
24
|
-
getSession:
|
|
24
|
+
getSession: getHarnessAuthenticationSession,
|
|
25
25
|
async startSession() {
|
|
26
|
-
const {
|
|
27
|
-
return
|
|
26
|
+
const { harness } = await readConfiguredHarness();
|
|
27
|
+
return startHarnessAuthenticationSession(harness);
|
|
28
28
|
},
|
|
29
|
-
writeSessionInput:
|
|
30
|
-
stopSession:
|
|
31
|
-
subscribeToSession:
|
|
29
|
+
writeSessionInput: writeHarnessAuthenticationSessionInput,
|
|
30
|
+
stopSession: stopHarnessAuthenticationSession,
|
|
31
|
+
subscribeToSession: subscribeToHarnessAuthenticationSession,
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
loadErrorMessage: 'Failed to load the authentication session.',
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
2
|
import { isUserGlobalAdmin } from '@/src/utils/isUserGlobalAdmin';
|
|
3
|
-
import {
|
|
3
|
+
import { readConfiguredHarness, resolveHarnessStatus } from '@/src/utils/harnessConfiguration';
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
applyVpsHarnessConfiguration,
|
|
6
6
|
updateVpsEnvironmentVariables,
|
|
7
7
|
} from '@/src/utils/vpsConfiguration';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Loads configured
|
|
10
|
+
* Loads configured harness settings from the editable VPS environment.
|
|
11
11
|
*/
|
|
12
12
|
export async function GET() {
|
|
13
13
|
if (!(await isUserGlobalAdmin())) {
|
|
@@ -15,22 +15,22 @@ export async function GET() {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
-
const
|
|
18
|
+
const configuredHarness = await readConfiguredHarness();
|
|
19
19
|
|
|
20
20
|
return NextResponse.json({
|
|
21
|
-
...
|
|
22
|
-
status: await
|
|
21
|
+
...configuredHarness,
|
|
22
|
+
status: await resolveHarnessStatus(configuredHarness.harness),
|
|
23
23
|
});
|
|
24
24
|
} catch (error) {
|
|
25
25
|
return NextResponse.json(
|
|
26
|
-
{ error: error instanceof Error ? error.message : 'Failed to load
|
|
26
|
+
{ error: error instanceof Error ? error.message : 'Failed to load harness configuration.' },
|
|
27
27
|
{ status: 500 },
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Updates
|
|
33
|
+
* Updates harness environment variables.
|
|
34
34
|
*/
|
|
35
35
|
export async function PATCH(request: Request) {
|
|
36
36
|
if (!(await isUserGlobalAdmin())) {
|
|
@@ -40,7 +40,7 @@ export async function PATCH(request: Request) {
|
|
|
40
40
|
try {
|
|
41
41
|
const body = (await request.json().catch(() => null)) as
|
|
42
42
|
| {
|
|
43
|
-
readonly
|
|
43
|
+
readonly harness?: string;
|
|
44
44
|
readonly model?: string;
|
|
45
45
|
readonly thinkingLevel?: string;
|
|
46
46
|
readonly applyRuntimeConfiguration?: boolean;
|
|
@@ -48,11 +48,11 @@ export async function PATCH(request: Request) {
|
|
|
48
48
|
| null;
|
|
49
49
|
|
|
50
50
|
if (!body) {
|
|
51
|
-
return NextResponse.json({ error: '
|
|
51
|
+
return NextResponse.json({ error: 'Harness payload is required.' }, { status: 400 });
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
await updateVpsEnvironmentVariables({
|
|
55
|
-
PTBK_HARNESS: body.
|
|
55
|
+
PTBK_HARNESS: body.harness || '',
|
|
56
56
|
PTBK_MODEL: body.model || '',
|
|
57
57
|
PTBK_THINKING_LEVEL: body.thinkingLevel || '',
|
|
58
58
|
});
|
|
@@ -62,11 +62,11 @@ export async function PATCH(request: Request) {
|
|
|
62
62
|
|
|
63
63
|
return NextResponse.json({
|
|
64
64
|
...payload,
|
|
65
|
-
applyResult: body.applyRuntimeConfiguration ? await
|
|
65
|
+
applyResult: body.applyRuntimeConfiguration ? await applyVpsHarnessConfiguration() : null,
|
|
66
66
|
});
|
|
67
67
|
} catch (error) {
|
|
68
68
|
return NextResponse.json(
|
|
69
|
-
{ error: error instanceof Error ? error.message : 'Failed to update
|
|
69
|
+
{ error: error instanceof Error ? error.message : 'Failed to update harness configuration.' },
|
|
70
70
|
{ status: 500 },
|
|
71
71
|
);
|
|
72
72
|
}
|
|
@@ -23,6 +23,7 @@ import type { AgentOrganizationAgent, AgentOrganizationFolder } from '../utils/a
|
|
|
23
23
|
import { getAgentNaming } from '../utils/getAgentNaming';
|
|
24
24
|
import { getCurrentUser } from '../utils/getCurrentUser';
|
|
25
25
|
import { getDefaultChatPreferences } from '../utils/chatPreferences';
|
|
26
|
+
import { formatServerLanguageHumanReadableDate } from '../utils/localization/formatServerLanguageHumanReadableDate';
|
|
26
27
|
import {
|
|
27
28
|
CHAT_VISUAL_MODE_COOKIE_NAME,
|
|
28
29
|
CHAT_VISUAL_MODE_METADATA_KEY,
|
|
@@ -51,6 +52,7 @@ import {
|
|
|
51
52
|
import { resolveFileUploadAvailability } from '../utils/upload/fileUploadAvailability';
|
|
52
53
|
import { readServerResourceWarningStatus } from '../utils/resourceMonitor/readServerResourceMonitorSnapshot';
|
|
53
54
|
import { RESOURCE_MONITOR_OK_WARNING_STATUS } from '../utils/resourceMonitor/resourceMonitorTypes';
|
|
55
|
+
import { readAgentsServerFooterVersion } from '../utils/vpsSelfUpdate/readAgentsServerFooterVersion';
|
|
54
56
|
import '@prisma/studio-core/ui/index.css';
|
|
55
57
|
import './globals.css';
|
|
56
58
|
|
|
@@ -288,6 +290,7 @@ export default async function RootLayout({
|
|
|
288
290
|
);
|
|
289
291
|
const chatPreferencesPromise = getDefaultChatPreferences();
|
|
290
292
|
const defaultIsNotificationsOnPromise = getDefaultIsNotificationsOn();
|
|
293
|
+
const footerVersionMetadataPromise = readAgentsServerFooterVersion();
|
|
291
294
|
const fileUploadAvailabilityPromise = providedServerPromise.then((providedServer) =>
|
|
292
295
|
resolveFileUploadAvailability({
|
|
293
296
|
serverId: providedServer.id,
|
|
@@ -364,6 +367,7 @@ export default async function RootLayout({
|
|
|
364
367
|
serverVisibility,
|
|
365
368
|
fileUploadAvailability,
|
|
366
369
|
resourceMonitorWarningStatus,
|
|
370
|
+
footerVersionMetadata,
|
|
367
371
|
] = await Promise.all([
|
|
368
372
|
isAdminPromise,
|
|
369
373
|
isGlobalAdminPromise,
|
|
@@ -383,6 +387,7 @@ export default async function RootLayout({
|
|
|
383
387
|
serverVisibilityPromise,
|
|
384
388
|
fileUploadAvailabilityPromise,
|
|
385
389
|
resourceMonitorWarningStatusPromise,
|
|
390
|
+
footerVersionMetadataPromise,
|
|
386
391
|
]);
|
|
387
392
|
|
|
388
393
|
const serverName = layoutMetadata.SERVER_NAME || 'Promptbook Agents Server';
|
|
@@ -411,6 +416,16 @@ export default async function RootLayout({
|
|
|
411
416
|
const defaultAgentAvatarVisualId = resolveDefaultAgentAvatarVisualId(rawDefaultAgentAvatarVisual);
|
|
412
417
|
const preferredLanguageSource = isServerLanguageEnforced ? rawServerLanguage : cookieLanguage || rawServerLanguage;
|
|
413
418
|
const serverLanguage = resolveServerLanguageCode(preferredLanguageSource);
|
|
419
|
+
const footerVersionReleasedAtLabel =
|
|
420
|
+
formatServerLanguageHumanReadableDate(footerVersionMetadata.releasedAt, serverLanguage, {
|
|
421
|
+
fallbackLabel: '',
|
|
422
|
+
}) || null;
|
|
423
|
+
const footerVersion = {
|
|
424
|
+
label: footerVersionMetadata.label,
|
|
425
|
+
url: footerVersionMetadata.versionUrl,
|
|
426
|
+
commitSha: footerVersionMetadata.currentCommitSha,
|
|
427
|
+
releasedAtLabel: footerVersionReleasedAtLabel,
|
|
428
|
+
};
|
|
414
429
|
const controlPanelOptionAvailability = getControlPanelOptionAvailability({
|
|
415
430
|
metadata: layoutMetadata,
|
|
416
431
|
isPushNotificationsConfigured: Boolean(webPushPublicKey),
|
|
@@ -447,6 +462,7 @@ export default async function RootLayout({
|
|
|
447
462
|
footerLinks={
|
|
448
463
|
isPublicServer ? footerLinks : footerLinks.filter((link) => link.url !== '/sitemap.xml')
|
|
449
464
|
}
|
|
465
|
+
footerVersion={footerVersion}
|
|
450
466
|
federatedServers={federatedServers}
|
|
451
467
|
defaultIsSoundsOn={chatPreferences.defaultIsSoundsOn}
|
|
452
468
|
defaultIsVibrationOn={chatPreferences.defaultIsVibrationOn}
|
|
@@ -105,6 +105,11 @@ type AdminTerminalCardProps<TSession extends AdminTerminalSession> = {
|
|
|
105
105
|
*/
|
|
106
106
|
readonly outputEmptyState: string;
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Whether the terminal output area should be mounted.
|
|
110
|
+
*/
|
|
111
|
+
readonly isOutputVisible?: boolean;
|
|
112
|
+
|
|
108
113
|
/**
|
|
109
114
|
* Optional shortcut buttons that send raw terminal input.
|
|
110
115
|
*/
|
|
@@ -136,6 +141,7 @@ export function AdminTerminalCard<TSession extends AdminTerminalSession>({
|
|
|
136
141
|
stopLabel,
|
|
137
142
|
outputLabel,
|
|
138
143
|
outputEmptyState,
|
|
144
|
+
isOutputVisible = true,
|
|
139
145
|
quickActions = [],
|
|
140
146
|
children,
|
|
141
147
|
}: AdminTerminalCardProps<TSession>) {
|
|
@@ -175,32 +181,34 @@ export function AdminTerminalCard<TSession extends AdminTerminalSession>({
|
|
|
175
181
|
|
|
176
182
|
{children}
|
|
177
183
|
|
|
178
|
-
|
|
179
|
-
<div className="
|
|
180
|
-
<
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
{isOutputVisible ? (
|
|
185
|
+
<div className="space-y-2">
|
|
186
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
187
|
+
<h3 className="text-sm font-semibold text-slate-700">{outputLabel}</h3>
|
|
188
|
+
{session ? (
|
|
189
|
+
<span className="text-xs text-slate-500">
|
|
190
|
+
{session.isRunning
|
|
191
|
+
? 'Running'
|
|
192
|
+
: session.exitCode === 0
|
|
193
|
+
? 'Finished successfully'
|
|
194
|
+
: 'Finished with an error'}
|
|
195
|
+
</span>
|
|
196
|
+
) : (
|
|
197
|
+
<span className="text-xs text-slate-500">No session started yet.</span>
|
|
198
|
+
)}
|
|
199
|
+
</div>
|
|
200
|
+
<AdminXtermTerminal
|
|
201
|
+
terminalId={session?.id || `${title}:empty`}
|
|
202
|
+
output={session?.output || ''}
|
|
203
|
+
emptyState={outputEmptyState}
|
|
204
|
+
isRunning={Boolean(session?.isRunning)}
|
|
205
|
+
ariaLabel={outputLabel}
|
|
206
|
+
onData={onSend}
|
|
207
|
+
/>
|
|
192
208
|
</div>
|
|
193
|
-
|
|
194
|
-
terminalId={session?.id || `${title}:empty`}
|
|
195
|
-
output={session?.output || ''}
|
|
196
|
-
emptyState={outputEmptyState}
|
|
197
|
-
isRunning={Boolean(session?.isRunning)}
|
|
198
|
-
ariaLabel={outputLabel}
|
|
199
|
-
onData={onSend}
|
|
200
|
-
/>
|
|
201
|
-
</div>
|
|
209
|
+
) : null}
|
|
202
210
|
|
|
203
|
-
{quickActions.length > 0 ? (
|
|
211
|
+
{isOutputVisible && quickActions.length > 0 ? (
|
|
204
212
|
<div className="flex flex-wrap gap-3">
|
|
205
213
|
{quickActions.map((quickAction) => (
|
|
206
214
|
<button
|