@flame0510/project-aether 1.3.0 → 1.4.0
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 -0
- package/agent-templates/README.md +42 -22
- package/agent-templates/base-image/Dockerfile +42 -33
- package/agent-templates/base-image/entrypoint.sh +67 -12
- package/app/agents/BrowserAccessSection.tsx +510 -0
- package/app/agents/ChannelManager.tsx +19 -11
- package/app/agents/ImageDownloadBanner.tsx +53 -19
- package/app/agents/ModelSection.tsx +4 -1
- package/app/agents/PageClient.tsx +629 -167
- package/app/agents/UpdateSection.tsx +300 -0
- package/app/agents/create/PageClient.tsx +11 -49
- package/app/api/agents/[id]/backup/route.ts +26 -69
- package/app/api/agents/[id]/channels/pairing/route.ts +3 -3
- package/app/api/agents/[id]/channels/telegram/route.ts +2 -2
- package/app/api/agents/[id]/cold-backup/route.ts +56 -0
- package/app/api/agents/[id]/devices/route.ts +126 -0
- package/app/api/agents/[id]/invite-link/route.ts +53 -0
- package/app/api/agents/[id]/lifecycle/route.ts +3 -0
- package/app/api/agents/[id]/open-control-ui/route.ts +58 -0
- package/app/api/agents/[id]/recreate/route.ts +33 -163
- package/app/api/agents/[id]/restart/route.ts +5 -0
- package/app/api/agents/[id]/restore/route.ts +40 -70
- package/app/api/agents/[id]/route.ts +38 -150
- package/app/api/agents/[id]/update/rollback/route.ts +30 -0
- package/app/api/agents/[id]/update/route.ts +50 -0
- package/app/api/agents/activity-summary/route.ts +67 -0
- package/app/api/agents/create/route.ts +32 -88
- package/app/api/agents/devices-summary/route.ts +37 -0
- package/app/api/agents/download-image/route.ts +16 -9
- package/app/api/agents/image-status/route.ts +31 -111
- package/app/api/agents/route.ts +25 -49
- package/app/api/agents/token/route.ts +33 -10
- package/app/api/assistant/route.ts +2 -2
- package/app/api/gateway/agent/route.ts +14 -0
- package/app/api/gateway/provider/balance/route.ts +5 -2
- package/app/api/gateway/sync.ts +97 -14
- package/app/api/setup/agent-image/route.ts +14 -42
- package/app/components/DashboardToolbar.tsx +1 -1
- package/app/gateway/PageClient.tsx +27 -32
- package/bin/rev4a.js +43 -41
- package/daemon.js +6 -6
- package/docs/ARCHITECTURE.md +95 -9
- package/docs/FRONTEND-ARCHITECTURE.md +8 -1
- package/docs/REV4A.md +54 -17
- package/docs/dev/API-REFERENCE.md +554 -100
- package/docs/dev/DATABASE.md +96 -0
- package/docs/dev/GATEWAY.md +21 -6
- package/docs/rag/DATA-FRESHNESS.md +6 -4
- package/docs/rag/GLOSSARY.md +12 -3
- package/docs/rag/REV4A-OVERVIEW.md +18 -5
- package/docs/rag/WHAT-I-CAN-ANSWER.md +6 -2
- package/instrumentation.ts +43 -0
- package/lib/agent-busy.ts +21 -0
- package/lib/agent-devices.ts +361 -0
- package/lib/agent-edit-state.ts +108 -0
- package/lib/agent-edit.ts +157 -0
- package/lib/agent-images.ts +375 -0
- package/lib/agent-ports-server.ts +27 -0
- package/lib/agent-ports.ts +68 -0
- package/lib/agent-recreate-state.ts +108 -0
- package/lib/agent-recreate.ts +305 -0
- package/lib/agent-restore-state.ts +107 -0
- package/lib/agent-restore.ts +135 -0
- package/lib/agent-setup.ts +66 -17
- package/lib/agent-update-state.ts +122 -0
- package/lib/agent-update.ts +448 -0
- package/lib/agent-versions.json +14 -0
- package/lib/agent-versions.ts +80 -0
- package/lib/buildAgentImage.ts +88 -290
- package/lib/channelManager.ts +149 -102
- package/lib/cold-backup.ts +354 -0
- package/lib/credentials/delivery.ts +3 -3
- package/lib/db-bootstrap.mjs +76 -0
- package/lib/docker-utils.ts +3 -3
- package/lib/provider-balance.ts +33 -12
- package/package.json +1 -1
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Agent base image banner on the Agents page.
|
|
5
|
+
*
|
|
6
|
+
* Shown when no supported OpenClaw version of the agent image is downloaded, when
|
|
7
|
+
* the registry publishes a newer supported version, and while a download runs.
|
|
8
|
+
* Downloading touches no agent: each agent keeps its OpenClaw version until it is
|
|
9
|
+
* updated on its own.
|
|
10
|
+
*/
|
|
3
11
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
4
12
|
import { Button } from '../components/ui';
|
|
5
13
|
|
|
@@ -7,16 +15,23 @@ interface ImageStatus {
|
|
|
7
15
|
exists: boolean;
|
|
8
16
|
needsUpdate: boolean;
|
|
9
17
|
downloading: boolean;
|
|
18
|
+
downloadingVersion: string | null;
|
|
19
|
+
localVersions: string[];
|
|
20
|
+
newestLocal: string | null;
|
|
21
|
+
/** Newest supported version on the registry that is not downloaded yet. */
|
|
22
|
+
available: string | null;
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
const POLL_MS = 2000;
|
|
13
26
|
|
|
14
27
|
export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: () => void }) {
|
|
15
|
-
const [status, setStatus] = useState<ImageStatus>({
|
|
28
|
+
const [status, setStatus] = useState<ImageStatus>({
|
|
29
|
+
exists: true, needsUpdate: false, downloading: false, downloadingVersion: null, localVersions: [], newestLocal: null, available: null,
|
|
30
|
+
});
|
|
16
31
|
const [error, setError] = useState<string | null>(null);
|
|
17
|
-
const [justCompleted, setJustCompleted] = useState(
|
|
32
|
+
const [justCompleted, setJustCompleted] = useState<string | null>(null);
|
|
18
33
|
const [actionLoading, setActionLoading] = useState(false);
|
|
19
|
-
const wasDownloadingRef = useRef(
|
|
34
|
+
const wasDownloadingRef = useRef<string | null>(null);
|
|
20
35
|
const completedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
21
36
|
|
|
22
37
|
useEffect(() => {
|
|
@@ -32,15 +47,20 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
32
47
|
|
|
33
48
|
if (data.downloading) {
|
|
34
49
|
setError(null);
|
|
50
|
+
wasDownloadingRef.current = data.downloadingVersion ?? wasDownloadingRef.current ?? 'image';
|
|
35
51
|
} else if (wasDownloadingRef.current) {
|
|
36
|
-
|
|
52
|
+
const finished = wasDownloadingRef.current;
|
|
53
|
+
wasDownloadingRef.current = null;
|
|
37
54
|
setActionLoading(false);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
if (data.localVersions?.includes(finished)) {
|
|
56
|
+
setJustCompleted(finished);
|
|
57
|
+
onImageReady?.();
|
|
58
|
+
if (completedTimerRef.current) clearTimeout(completedTimerRef.current);
|
|
59
|
+
completedTimerRef.current = setTimeout(() => setJustCompleted(null), 3000);
|
|
60
|
+
} else {
|
|
61
|
+
setError(`The OpenClaw ${finished} download did not complete. Check the Docker connection and try again.`);
|
|
62
|
+
}
|
|
41
63
|
}
|
|
42
|
-
|
|
43
|
-
wasDownloadingRef.current = data.downloading;
|
|
44
64
|
} catch { /* ignore */ }
|
|
45
65
|
|
|
46
66
|
if (active) setTimeout(tick, POLL_MS);
|
|
@@ -56,16 +76,23 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
56
76
|
};
|
|
57
77
|
}, []);
|
|
58
78
|
|
|
59
|
-
const handleDownload = useCallback(async () => {
|
|
79
|
+
const handleDownload = useCallback(async (version: string | null) => {
|
|
60
80
|
setActionLoading(true);
|
|
61
81
|
setError(null);
|
|
62
82
|
try {
|
|
63
|
-
const res = await fetch('/api/agents/download-image', {
|
|
83
|
+
const res = await fetch('/api/agents/download-image', {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: { 'Content-Type': 'application/json' },
|
|
86
|
+
body: JSON.stringify(version ? { version } : {}),
|
|
87
|
+
});
|
|
64
88
|
if (!res.ok) {
|
|
65
89
|
const data = await res.json().catch(() => ({ error: 'Failed to start download' }));
|
|
66
90
|
setError(data.error || 'Failed to start download');
|
|
67
91
|
setActionLoading(false);
|
|
92
|
+
return;
|
|
68
93
|
}
|
|
94
|
+
const data = await res.json().catch(() => ({})) as { version?: string };
|
|
95
|
+
wasDownloadingRef.current = data.version ?? version ?? 'image';
|
|
69
96
|
} catch (e: unknown) {
|
|
70
97
|
setError((e as Error).message || 'Download failed');
|
|
71
98
|
setActionLoading(false);
|
|
@@ -79,6 +106,7 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
79
106
|
|
|
80
107
|
// ─── Downloading ───
|
|
81
108
|
if (isDownloading) {
|
|
109
|
+
const version = status.downloadingVersion ?? status.available;
|
|
82
110
|
return (
|
|
83
111
|
<Banner downloading>
|
|
84
112
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flex: 1 }}>
|
|
@@ -89,7 +117,9 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
89
117
|
animation: 'pulse 1.5s ease-in-out infinite',
|
|
90
118
|
flexShrink: 0,
|
|
91
119
|
}} />
|
|
92
|
-
<span style={{ color: 'var(--violet)', fontWeight: 500 }}>
|
|
120
|
+
<span style={{ color: 'var(--violet)', fontWeight: 500 }}>
|
|
121
|
+
{version ? `Downloading the OpenClaw ${version} agent image…` : 'Downloading the agent image…'}
|
|
122
|
+
</span>
|
|
93
123
|
</div>
|
|
94
124
|
<div style={{
|
|
95
125
|
flex: 1, maxWidth: 120, height: 3,
|
|
@@ -107,11 +137,11 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
107
137
|
}
|
|
108
138
|
|
|
109
139
|
// ─── Just completed ───
|
|
110
|
-
if (justCompleted
|
|
140
|
+
if (justCompleted) {
|
|
111
141
|
return (
|
|
112
142
|
<Banner success>
|
|
113
143
|
<div style={{ flex: 1 }}>
|
|
114
|
-
<span style={{ color: '#22c55e', fontWeight: 500 }}>✓
|
|
144
|
+
<span style={{ color: '#22c55e', fontWeight: 500 }}>✓ OpenClaw {justCompleted} agent image ready</span>
|
|
115
145
|
</div>
|
|
116
146
|
</Banner>
|
|
117
147
|
);
|
|
@@ -122,16 +152,20 @@ export default function ImageDownloadBanner({ onImageReady }: { onImageReady?: (
|
|
|
122
152
|
<Banner warning>
|
|
123
153
|
<div style={{ flex: 1 }}>
|
|
124
154
|
<strong style={{ color: '#fbbf24' }}>
|
|
125
|
-
{status.exists
|
|
155
|
+
{status.exists
|
|
156
|
+
? `⚠ OpenClaw ${status.available} is available.`
|
|
157
|
+
: '⚠ Agent base image not found.'}
|
|
126
158
|
</strong>
|
|
127
159
|
{' '}{status.exists
|
|
128
|
-
? '
|
|
160
|
+
? 'Download it for new agents; existing agents keep their version until they are updated.'
|
|
129
161
|
: 'Download it to create agent containers.'}
|
|
130
162
|
{error && <div style={{ color: '#ef4444', fontSize: 11, marginTop: 4 }}>{error}</div>}
|
|
131
163
|
</div>
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
|
|
164
|
+
{(status.exists ? status.available : true) && (
|
|
165
|
+
<Button variant="primary" size="sm" onClick={() => { void handleDownload(status.available); }} loading={actionLoading}>
|
|
166
|
+
{actionLoading ? 'Starting…' : status.exists ? `Download ${status.available}` : 'Download Image'}
|
|
167
|
+
</Button>
|
|
168
|
+
)}
|
|
135
169
|
</Banner>
|
|
136
170
|
);
|
|
137
171
|
}
|
|
@@ -68,10 +68,13 @@ function optionLabel(m: ModelOption): string {
|
|
|
68
68
|
export default function ModelSection({
|
|
69
69
|
agentId,
|
|
70
70
|
onSaved,
|
|
71
|
+
agentBusy,
|
|
71
72
|
}: {
|
|
72
73
|
agentId: string;
|
|
73
74
|
/** Called after a successful save so the agent list can refresh its chip. */
|
|
74
75
|
onSaved?: () => void;
|
|
76
|
+
/** A long operation is running on this agent: saving would race it. */
|
|
77
|
+
agentBusy?: boolean;
|
|
75
78
|
}) {
|
|
76
79
|
const [groups, setGroups] = useState<ProviderGroup[]>([]);
|
|
77
80
|
const [current, setCurrent] = useState<AgentModel | null>(null);
|
|
@@ -293,7 +296,7 @@ export default function ModelSection({
|
|
|
293
296
|
</div>
|
|
294
297
|
|
|
295
298
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
|
|
296
|
-
<Button variant="primary" size="sm" loading={saving} disabled={!dirty || !primary || primaryUnavailable} onClick={save}>
|
|
299
|
+
<Button variant="primary" size="sm" loading={saving} disabled={!dirty || !primary || primaryUnavailable || !!agentBusy} onClick={save}>
|
|
297
300
|
{saving ? 'Saving…' : 'Save model'}
|
|
298
301
|
</Button>
|
|
299
302
|
{dirty && !saving && (
|