@flame0510/project-aether 1.8.0 → 1.9.1
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/README.md +1 -1
- package/app/agents/PageClient.tsx +3 -0
- package/app/agents/create/loading.tsx +51 -0
- package/app/agents/loading.tsx +2 -11
- package/app/api/agents/create/route.ts +6 -0
- package/app/api/assistant/route.ts +1 -1
- package/app/components/Skeleton.tsx +45 -0
- package/app/config/loading.tsx +4 -9
- package/app/containers/ContainersClient.tsx +4 -2
- package/app/containers/loading.tsx +2 -18
- package/app/containers/terminal/[id]/TerminalClient.tsx +230 -276
- package/app/containers/terminal/[id]/loading.tsx +20 -0
- package/app/crons/loading.tsx +4 -9
- package/app/gateway/loading.tsx +3 -7
- package/app/globals.css +10 -0
- package/app/lineage/loading.tsx +4 -9
- package/app/memory/loading.tsx +4 -9
- package/app/plugins/loading.tsx +4 -9
- package/app/skills/loading.tsx +4 -9
- package/app/tools/loading.tsx +4 -9
- package/bin/rev4a.js +8 -4
- package/docs/ARCHITECTURE.md +14 -24
- package/docs/CONTAINER-TERMINAL.md +169 -261
- package/docs/FRONTEND-ARCHITECTURE.md +2 -1
- package/docs/REV4A.md +4 -2
- package/docs/dev/API-REFERENCE.md +21 -5
- package/docs/rag/GLOSSARY.md +1 -1
- package/docs/rag/REV4A-OVERVIEW.md +1 -1
- package/lib/agent-recreate.ts +3 -0
- package/package.json +5 -1
- package/server.js +183 -0
- package/terminal-ws-server.js +329 -96
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ already filled in — you normally never set these by hand. See
|
|
|
74
74
|
| Variable | Default | Required | Description |
|
|
75
75
|
|---|---|---|---|
|
|
76
76
|
| `PORT` | `3740` | No | Dashboard HTTP port |
|
|
77
|
-
| `TERMINAL_WS_PORT` | `3741` | No |
|
|
77
|
+
| `TERMINAL_WS_PORT` | `3741` | No | Loopback port of the container terminal server (the browser uses the dashboard's port) |
|
|
78
78
|
| `REV4A_PASSWORD` | — | Yes | Dashboard login password (set via the setup wizard) |
|
|
79
79
|
| `REV4A_TOKEN` | auto-generated | Yes | Bearer token for API routes and agent calls |
|
|
80
80
|
| `REV4A_JWT_SECRET` | auto-generated | Yes | Secret for auth cookies |
|
|
@@ -284,6 +284,9 @@ function AgentListItem({
|
|
|
284
284
|
|
|
285
285
|
export default function AgentsPageClient() {
|
|
286
286
|
const router = useRouter();
|
|
287
|
+
// "+ New" navigates with router.push, which prefetches nothing: warm the route up front,
|
|
288
|
+
// so the click shows Create Agent (or its loading frame) at once.
|
|
289
|
+
useEffect(() => { router.prefetch('/agents/create'); }, [router]);
|
|
287
290
|
const [agents, setAgents] = useState<Agent[]>([]);
|
|
288
291
|
const [loading, setLoading] = useState(true);
|
|
289
292
|
const [error, setError] = useState('');
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Skeleton } from '../../components/Skeleton';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create Agent while the route loads: the template step's own frame (same wrappers,
|
|
5
|
+
* paddings and font boxes as PageClient.tsx), not the Agents list's rows — so opening it
|
|
6
|
+
* from "+ New" shows its shape at once and nothing moves when it lands.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function Line({ width, size }: { width: number | string; size: number }) {
|
|
10
|
+
return <div style={{ fontSize: size }}><Skeleton as="span" inline width={width} height={Math.round(size * 0.8)} /></div>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** One card: an avatar, a name line, a description (one or two lines). */
|
|
14
|
+
function Card({ square, lines, padding }: { square: boolean; lines: number; padding: string }) {
|
|
15
|
+
return (
|
|
16
|
+
<div style={{ display: 'flex', alignItems: square ? 'flex-start' : 'center', gap: 12, padding, border: '1px solid var(--border)' }}>
|
|
17
|
+
<Skeleton width={square ? 32 : 28} height={square ? 32 : 28} variant={square ? 'block' : 'circle'} />
|
|
18
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
19
|
+
<Line width={110} size={14} />
|
|
20
|
+
<div style={{ marginTop: 2 }}>
|
|
21
|
+
{Array.from({ length: lines }).map((_, i) => <Line key={i} width={i === lines - 1 ? '45%' : '92%'} size={11} />)}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function CreateAgentLoading() {
|
|
29
|
+
return (
|
|
30
|
+
<div aria-busy="true" style={{ minHeight: '100dvh', overflowY: 'auto', paddingBottom: 72, boxSizing: 'border-box', width: '100%' }}>
|
|
31
|
+
<div style={{ width: '100%', maxWidth: 760, margin: '0 auto', padding: '24px 16px', boxSizing: 'border-box' }}>
|
|
32
|
+
<div style={{ fontSize: 20, fontWeight: 500, marginBottom: 24 }}>
|
|
33
|
+
<Skeleton as="span" inline width={150} height={20} accent />
|
|
34
|
+
</div>
|
|
35
|
+
<div style={{ fontSize: 12, marginBottom: 16 }}><Skeleton as="span" inline width={230} height={10} /></div>
|
|
36
|
+
<div style={{ marginBottom: 24 }}>
|
|
37
|
+
<div style={{ fontSize: 10, marginBottom: 8 }}><Skeleton as="span" inline width={130} height={8} /></div>
|
|
38
|
+
<Card square lines={2} padding="16px" />
|
|
39
|
+
</div>
|
|
40
|
+
<div style={{ fontSize: 10, marginBottom: 8 }}><Skeleton as="span" inline width={140} height={8} /></div>
|
|
41
|
+
<div style={{ display: 'grid', gap: 8 }}>
|
|
42
|
+
{[0, 1, 2].map((i) => <Card key={i} square={false} lines={1} padding="14px 16px" />)}
|
|
43
|
+
</div>
|
|
44
|
+
<div style={{ marginTop: 24, display: 'flex', justifyContent: 'space-between' }}>
|
|
45
|
+
<Skeleton width={70} height={28} />
|
|
46
|
+
<Skeleton width={62} height={28} accent />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
package/app/agents/loading.tsx
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ListPageSkeleton } from '../components/Skeleton';
|
|
2
2
|
|
|
3
3
|
export default function AgentsLoading() {
|
|
4
|
-
return
|
|
5
|
-
<div style={{
|
|
6
|
-
padding: '24px 20px',
|
|
7
|
-
maxWidth: 1080,
|
|
8
|
-
margin: '0 auto',
|
|
9
|
-
}}>
|
|
10
|
-
<Skeleton width={120} height={24} accent style={{ marginBottom: 20 }} />
|
|
11
|
-
<CardRowSkeleton count={5} height={56} />
|
|
12
|
-
</div>
|
|
13
|
-
);
|
|
4
|
+
return <ListPageSkeleton rows={5} rowHeight={56} />;
|
|
14
5
|
}
|
|
@@ -221,7 +221,13 @@ export async function POST(request: NextRequest): Promise<NextResponse> {
|
|
|
221
221
|
// this is intentional (e.g. BOOTSTRAP.md only exists in custom/).
|
|
222
222
|
const templateFiles = ['AGENTS.md', 'SOUL.md', 'IDENTITY.md', 'MEMORY.md', 'TOOLS.md', 'HEARTBEAT.md', 'BOOTSTRAP.md'];
|
|
223
223
|
|
|
224
|
+
// --init: Docker's tini runs as PID 1 and collects orphaned processes. Without it
|
|
225
|
+
// openclaw-gateway is PID 1 and never does, so every process a tool leaves behind
|
|
226
|
+
// (Chrome, builds, scripts) stays a zombie and counts against the container's pids
|
|
227
|
+
// limit until the container can no longer start anything. Same flag in
|
|
228
|
+
// lib/agent-recreate.ts.
|
|
224
229
|
const cmd = `docker run -d \
|
|
230
|
+
--init \
|
|
225
231
|
--name "${containerName}" \
|
|
226
232
|
--network "${network}" \
|
|
227
233
|
--restart unless-stopped \
|
|
@@ -60,7 +60,7 @@ const PAGE_CONTEXT: Record<string, PageContext> = {
|
|
|
60
60
|
},
|
|
61
61
|
'/containers/terminal/[id]': {
|
|
62
62
|
title: 'Container Terminal',
|
|
63
|
-
description: 'Interactive web terminal for a specific Docker container.
|
|
63
|
+
description: 'Interactive web terminal for a specific Docker container: a real shell (Tab completion, history, top, vi, resizing). A dropped connection is resumed for 2 minutes; Close ends the shell.',
|
|
64
64
|
actions: ['Run shell commands inside the container', 'Inspect files, processes, and logs live', 'Use any CLI tool available inside the container'],
|
|
65
65
|
hints: 'This is a live terminal — changes you make inside the container are real. Be careful with destructive commands.',
|
|
66
66
|
},
|
|
@@ -337,3 +337,48 @@ export function AgentSyncRowsSkeleton({ rows = 3 }: { rows?: number }) {
|
|
|
337
337
|
</div>
|
|
338
338
|
);
|
|
339
339
|
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* The frame of a route's `loading.tsx`: full width up to `maxWidth`, centred, scrollable.
|
|
343
|
+
*
|
|
344
|
+
* `width: 100%` is the point. The app shell's content area is a column flexbox, and a
|
|
345
|
+
* child with `margin: 0 auto` but no width is not stretched by it — it shrinks to its
|
|
346
|
+
* content. Every loader used to do exactly that, so its only fixed-width piece (a 120 px
|
|
347
|
+
* title bar) set the width of everything, and the rows came out 120 px wide.
|
|
348
|
+
*/
|
|
349
|
+
export function RouteLoadingShell({ children, maxWidth = 1080, padding = '24px 20px' }: {
|
|
350
|
+
children: React.ReactNode;
|
|
351
|
+
maxWidth?: number;
|
|
352
|
+
padding?: string;
|
|
353
|
+
}) {
|
|
354
|
+
return (
|
|
355
|
+
<div
|
|
356
|
+
aria-busy="true"
|
|
357
|
+
style={{
|
|
358
|
+
width: '100%',
|
|
359
|
+
maxWidth,
|
|
360
|
+
margin: '0 auto',
|
|
361
|
+
padding,
|
|
362
|
+
boxSizing: 'border-box',
|
|
363
|
+
height: '100%',
|
|
364
|
+
overflowY: 'auto',
|
|
365
|
+
}}
|
|
366
|
+
>
|
|
367
|
+
{children}
|
|
368
|
+
</div>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** A title bar over a list of rows — the loader of the list pages. */
|
|
373
|
+
export function ListPageSkeleton({ rows = 5, rowHeight = 48, titleWidth = 120 }: {
|
|
374
|
+
rows?: number;
|
|
375
|
+
rowHeight?: number;
|
|
376
|
+
titleWidth?: number;
|
|
377
|
+
}) {
|
|
378
|
+
return (
|
|
379
|
+
<RouteLoadingShell>
|
|
380
|
+
<Skeleton width={titleWidth} height={20} accent style={{ marginBottom: 20 }} />
|
|
381
|
+
<CardRowSkeleton count={rows} height={rowHeight} />
|
|
382
|
+
</RouteLoadingShell>
|
|
383
|
+
);
|
|
384
|
+
}
|
package/app/config/loading.tsx
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ListPageSkeleton } from '../components/Skeleton';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
<CardRowSkeleton count={5} height={48} />
|
|
7
|
-
</div>
|
|
8
|
-
);
|
|
9
|
-
|
|
10
|
-
export default PageSkeleton;
|
|
3
|
+
export default function ConfigLoading() {
|
|
4
|
+
return <ListPageSkeleton />;
|
|
5
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
4
5
|
import { Button, Pill } from '../components/ui';
|
|
5
6
|
|
|
6
7
|
interface ContainerInfo {
|
|
@@ -85,7 +86,8 @@ export default function ContainersClient() {
|
|
|
85
86
|
{c.state === 'running' ? c.status.replace(/^Up\s+/, '') : c.status}
|
|
86
87
|
</span>
|
|
87
88
|
{c.state === 'running' && (
|
|
88
|
-
|
|
89
|
+
// A client-side navigation (prefetched), not a full page reload.
|
|
90
|
+
<Link
|
|
89
91
|
href={`/containers/terminal/${encodeURIComponent(c.name)}`}
|
|
90
92
|
style={{
|
|
91
93
|
fontSize: 10,
|
|
@@ -100,7 +102,7 @@ export default function ContainersClient() {
|
|
|
100
102
|
}}
|
|
101
103
|
>
|
|
102
104
|
TERMINAL
|
|
103
|
-
</
|
|
105
|
+
</Link>
|
|
104
106
|
)}
|
|
105
107
|
</div>
|
|
106
108
|
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ListPageSkeleton } from '../components/Skeleton';
|
|
2
2
|
|
|
3
3
|
export default function ContainersLoading() {
|
|
4
|
-
return
|
|
5
|
-
<div style={{
|
|
6
|
-
padding: '16px 20px',
|
|
7
|
-
maxWidth: 1080,
|
|
8
|
-
margin: '0 auto',
|
|
9
|
-
height: '100%',
|
|
10
|
-
boxSizing: 'border-box',
|
|
11
|
-
overflowY: 'auto',
|
|
12
|
-
WebkitOverflowScrolling: 'touch',
|
|
13
|
-
paddingBottom: 72,
|
|
14
|
-
}}>
|
|
15
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
|
|
16
|
-
<Skeleton width={140} height={20} accent />
|
|
17
|
-
</div>
|
|
18
|
-
<CardRowSkeleton count={4} height={56} />
|
|
19
|
-
</div>
|
|
20
|
-
);
|
|
4
|
+
return <ListPageSkeleton rows={4} rowHeight={56} titleWidth={140} />;
|
|
21
5
|
}
|