clawport-ui 0.1.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/.env.example +35 -0
- package/BRANDING.md +131 -0
- package/CLAUDE.md +252 -0
- package/README.md +262 -0
- package/SETUP.md +337 -0
- package/app/agents/[id]/page.tsx +727 -0
- package/app/api/agents/route.ts +12 -0
- package/app/api/chat/[id]/route.ts +139 -0
- package/app/api/cron-runs/route.ts +13 -0
- package/app/api/crons/route.ts +12 -0
- package/app/api/kanban/chat/[id]/route.ts +119 -0
- package/app/api/kanban/chat-history/[ticketId]/route.ts +36 -0
- package/app/api/memory/route.ts +12 -0
- package/app/api/transcribe/route.ts +37 -0
- package/app/api/tts/route.ts +42 -0
- package/app/chat/[id]/page.tsx +10 -0
- package/app/chat/page.tsx +200 -0
- package/app/crons/page.tsx +870 -0
- package/app/docs/page.tsx +399 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +692 -0
- package/app/kanban/page.tsx +327 -0
- package/app/layout.tsx +45 -0
- package/app/memory/page.tsx +685 -0
- package/app/page.tsx +817 -0
- package/app/providers.tsx +37 -0
- package/app/settings/page.tsx +901 -0
- package/app/settings-provider.tsx +209 -0
- package/components/AgentAvatar.tsx +54 -0
- package/components/AgentNode.tsx +122 -0
- package/components/Breadcrumbs.tsx +126 -0
- package/components/DynamicFavicon.tsx +62 -0
- package/components/ErrorState.tsx +97 -0
- package/components/FeedView.tsx +494 -0
- package/components/GlobalSearch.tsx +571 -0
- package/components/GridView.tsx +532 -0
- package/components/ManorMap.tsx +157 -0
- package/components/MobileSidebar.tsx +251 -0
- package/components/NavLinks.tsx +271 -0
- package/components/OnboardingWizard.tsx +1067 -0
- package/components/Sidebar.tsx +115 -0
- package/components/ThemeToggle.tsx +108 -0
- package/components/chat/AgentList.tsx +537 -0
- package/components/chat/ConversationView.tsx +1047 -0
- package/components/chat/FileAttachment.tsx +140 -0
- package/components/chat/MediaPreview.tsx +111 -0
- package/components/chat/VoiceMessage.tsx +139 -0
- package/components/crons/PipelineGraph.tsx +327 -0
- package/components/crons/WeeklySchedule.tsx +630 -0
- package/components/docs/AgentsSection.tsx +209 -0
- package/components/docs/ApiReferenceSection.tsx +256 -0
- package/components/docs/ArchitectureSection.tsx +221 -0
- package/components/docs/ComponentsSection.tsx +253 -0
- package/components/docs/CronSystemSection.tsx +235 -0
- package/components/docs/DocSection.tsx +346 -0
- package/components/docs/GettingStartedSection.tsx +169 -0
- package/components/docs/ThemingSection.tsx +257 -0
- package/components/docs/TroubleshootingSection.tsx +200 -0
- package/components/kanban/AgentPicker.tsx +321 -0
- package/components/kanban/CreateTicketModal.tsx +333 -0
- package/components/kanban/KanbanBoard.tsx +70 -0
- package/components/kanban/KanbanColumn.tsx +166 -0
- package/components/kanban/TicketCard.tsx +245 -0
- package/components/kanban/TicketDetailPanel.tsx +850 -0
- package/components/ui/badge.tsx +48 -0
- package/components/ui/button.tsx +64 -0
- package/components/ui/card.tsx +92 -0
- package/components/ui/dialog.tsx +158 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/separator.tsx +28 -0
- package/components/ui/skeleton.tsx +27 -0
- package/components/ui/tabs.tsx +91 -0
- package/components/ui/tooltip.tsx +57 -0
- package/components.json +23 -0
- package/docs/API.md +648 -0
- package/docs/COMPONENTS.md +1059 -0
- package/docs/THEMING.md +795 -0
- package/lib/agents-registry.ts +35 -0
- package/lib/agents.json +282 -0
- package/lib/agents.test.ts +367 -0
- package/lib/agents.ts +32 -0
- package/lib/anthropic.test.ts +422 -0
- package/lib/anthropic.ts +220 -0
- package/lib/api-error.ts +16 -0
- package/lib/audio-recorder.test.ts +72 -0
- package/lib/audio-recorder.ts +169 -0
- package/lib/conversations.test.ts +331 -0
- package/lib/conversations.ts +117 -0
- package/lib/cron-pipelines.test.ts +69 -0
- package/lib/cron-pipelines.ts +58 -0
- package/lib/cron-runs.test.ts +118 -0
- package/lib/cron-runs.ts +67 -0
- package/lib/cron-utils.test.ts +222 -0
- package/lib/cron-utils.ts +160 -0
- package/lib/crons.test.ts +502 -0
- package/lib/crons.ts +114 -0
- package/lib/env.test.ts +44 -0
- package/lib/env.ts +14 -0
- package/lib/kanban/automation.test.ts +245 -0
- package/lib/kanban/automation.ts +143 -0
- package/lib/kanban/chat-store.test.ts +149 -0
- package/lib/kanban/chat-store.ts +81 -0
- package/lib/kanban/store.test.ts +238 -0
- package/lib/kanban/store.ts +98 -0
- package/lib/kanban/types.ts +50 -0
- package/lib/kanban/useAgentWork.ts +78 -0
- package/lib/memory.ts +45 -0
- package/lib/multimodal.test.ts +219 -0
- package/lib/multimodal.ts +68 -0
- package/lib/pipeline.integration.test.ts +343 -0
- package/lib/sanitize.ts +194 -0
- package/lib/settings.test.ts +137 -0
- package/lib/settings.ts +94 -0
- package/lib/styles.ts +24 -0
- package/lib/themes.ts +9 -0
- package/lib/transcribe.test.ts +141 -0
- package/lib/transcribe.ts +111 -0
- package/lib/types.ts +66 -0
- package/lib/utils.ts +6 -0
- package/lib/validation.test.ts +132 -0
- package/lib/validation.ts +80 -0
- package/next.config.ts +7 -0
- package/package.json +56 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/setup.mjs +215 -0
- package/tsconfig.json +34 -0
- package/vitest.config.ts +17 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import type { ReactNode, CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
/* ─── Headings ─────────────────────────────────────────────────── */
|
|
4
|
+
|
|
5
|
+
export function Heading({ children }: { children: ReactNode }) {
|
|
6
|
+
return (
|
|
7
|
+
<h2
|
|
8
|
+
style={{
|
|
9
|
+
fontSize: "var(--text-title2)",
|
|
10
|
+
fontWeight: "var(--weight-bold)",
|
|
11
|
+
color: "var(--text-primary)",
|
|
12
|
+
lineHeight: "var(--leading-snug)",
|
|
13
|
+
marginTop: "var(--space-10)",
|
|
14
|
+
marginBottom: "var(--space-4)",
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
{children}
|
|
18
|
+
</h2>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function SubHeading({ children }: { children: ReactNode }) {
|
|
23
|
+
return (
|
|
24
|
+
<h3
|
|
25
|
+
style={{
|
|
26
|
+
fontSize: "var(--text-body)",
|
|
27
|
+
fontWeight: "var(--weight-semibold)",
|
|
28
|
+
color: "var(--text-primary)",
|
|
29
|
+
lineHeight: "var(--leading-snug)",
|
|
30
|
+
marginTop: "var(--space-8)",
|
|
31
|
+
marginBottom: "var(--space-3)",
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</h3>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* ─── Text ─────────────────────────────────────────────────────── */
|
|
40
|
+
|
|
41
|
+
export function Paragraph({ children }: { children: ReactNode }) {
|
|
42
|
+
return (
|
|
43
|
+
<p
|
|
44
|
+
style={{
|
|
45
|
+
fontSize: "var(--text-subheadline)",
|
|
46
|
+
lineHeight: "var(--leading-relaxed)",
|
|
47
|
+
color: "var(--text-secondary)",
|
|
48
|
+
marginBottom: "var(--space-4)",
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</p>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* ─── Code ─────────────────────────────────────────────────────── */
|
|
57
|
+
|
|
58
|
+
export function CodeBlock({
|
|
59
|
+
children,
|
|
60
|
+
title,
|
|
61
|
+
}: {
|
|
62
|
+
children: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
}) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
style={{
|
|
68
|
+
background: "var(--code-bg)",
|
|
69
|
+
border: "1px solid var(--code-border)",
|
|
70
|
+
borderRadius: "var(--radius-md)",
|
|
71
|
+
marginBottom: "var(--space-4)",
|
|
72
|
+
overflow: "hidden",
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
{title && (
|
|
76
|
+
<div
|
|
77
|
+
style={{
|
|
78
|
+
padding: "var(--space-2) var(--space-4)",
|
|
79
|
+
borderBottom: "1px solid var(--code-border)",
|
|
80
|
+
fontSize: "var(--text-caption1)",
|
|
81
|
+
fontWeight: "var(--weight-semibold)",
|
|
82
|
+
color: "var(--text-tertiary)",
|
|
83
|
+
fontFamily: "var(--font-mono)",
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
{title}
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
<pre
|
|
90
|
+
style={{
|
|
91
|
+
padding: "var(--space-4)",
|
|
92
|
+
margin: 0,
|
|
93
|
+
fontSize: "var(--text-footnote)",
|
|
94
|
+
lineHeight: "var(--leading-relaxed)",
|
|
95
|
+
color: "var(--code-text)",
|
|
96
|
+
fontFamily: "var(--font-mono)",
|
|
97
|
+
whiteSpace: "pre-wrap",
|
|
98
|
+
overflowX: "auto",
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
{children}
|
|
102
|
+
</pre>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function InlineCode({ children }: { children: ReactNode }) {
|
|
108
|
+
return (
|
|
109
|
+
<code
|
|
110
|
+
style={{
|
|
111
|
+
background: "var(--code-bg)",
|
|
112
|
+
border: "1px solid var(--code-border)",
|
|
113
|
+
borderRadius: "4px",
|
|
114
|
+
padding: "1px 6px",
|
|
115
|
+
fontSize: "0.9em",
|
|
116
|
+
fontFamily: "var(--font-mono)",
|
|
117
|
+
color: "var(--code-text)",
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{children}
|
|
121
|
+
</code>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* ─── Cards & Panels ───────────────────────────────────────────── */
|
|
126
|
+
|
|
127
|
+
export function InfoCard({
|
|
128
|
+
title,
|
|
129
|
+
children,
|
|
130
|
+
style,
|
|
131
|
+
}: {
|
|
132
|
+
title?: string;
|
|
133
|
+
children: ReactNode;
|
|
134
|
+
style?: CSSProperties;
|
|
135
|
+
}) {
|
|
136
|
+
return (
|
|
137
|
+
<div
|
|
138
|
+
style={{
|
|
139
|
+
background: "var(--material-regular)",
|
|
140
|
+
border: "1px solid var(--separator)",
|
|
141
|
+
borderRadius: "var(--radius-md)",
|
|
142
|
+
padding: "var(--space-4) var(--space-5)",
|
|
143
|
+
marginBottom: "var(--space-4)",
|
|
144
|
+
...style,
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{title && (
|
|
148
|
+
<div
|
|
149
|
+
style={{
|
|
150
|
+
fontSize: "var(--text-subheadline)",
|
|
151
|
+
fontWeight: "var(--weight-semibold)",
|
|
152
|
+
color: "var(--text-primary)",
|
|
153
|
+
marginBottom: "var(--space-3)",
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
{title}
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
{children}
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* ─── Tables ───────────────────────────────────────────────────── */
|
|
165
|
+
|
|
166
|
+
export function Table({
|
|
167
|
+
headers,
|
|
168
|
+
rows,
|
|
169
|
+
}: {
|
|
170
|
+
headers: string[];
|
|
171
|
+
rows: (string | ReactNode)[][];
|
|
172
|
+
}) {
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
style={{
|
|
176
|
+
overflowX: "auto",
|
|
177
|
+
marginBottom: "var(--space-4)",
|
|
178
|
+
border: "1px solid var(--separator)",
|
|
179
|
+
borderRadius: "var(--radius-md)",
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<table
|
|
183
|
+
style={{
|
|
184
|
+
width: "100%",
|
|
185
|
+
borderCollapse: "collapse",
|
|
186
|
+
fontSize: "var(--text-footnote)",
|
|
187
|
+
lineHeight: "var(--leading-normal)",
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
<thead>
|
|
191
|
+
<tr>
|
|
192
|
+
{headers.map((h, i) => (
|
|
193
|
+
<th
|
|
194
|
+
key={i}
|
|
195
|
+
style={{
|
|
196
|
+
textAlign: "left",
|
|
197
|
+
padding: "var(--space-2) var(--space-3)",
|
|
198
|
+
fontWeight: "var(--weight-semibold)",
|
|
199
|
+
color: "var(--text-primary)",
|
|
200
|
+
background: "var(--material-thin)",
|
|
201
|
+
borderBottom: "1px solid var(--separator)",
|
|
202
|
+
whiteSpace: "nowrap",
|
|
203
|
+
}}
|
|
204
|
+
>
|
|
205
|
+
{h}
|
|
206
|
+
</th>
|
|
207
|
+
))}
|
|
208
|
+
</tr>
|
|
209
|
+
</thead>
|
|
210
|
+
<tbody>
|
|
211
|
+
{rows.map((row, ri) => (
|
|
212
|
+
<tr key={ri}>
|
|
213
|
+
{row.map((cell, ci) => (
|
|
214
|
+
<td
|
|
215
|
+
key={ci}
|
|
216
|
+
style={{
|
|
217
|
+
padding: "var(--space-2) var(--space-3)",
|
|
218
|
+
color: "var(--text-secondary)",
|
|
219
|
+
borderBottom:
|
|
220
|
+
ri < rows.length - 1
|
|
221
|
+
? "1px solid var(--separator)"
|
|
222
|
+
: undefined,
|
|
223
|
+
verticalAlign: "top",
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
{cell}
|
|
227
|
+
</td>
|
|
228
|
+
))}
|
|
229
|
+
</tr>
|
|
230
|
+
))}
|
|
231
|
+
</tbody>
|
|
232
|
+
</table>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ─── Lists ────────────────────────────────────────────────────── */
|
|
238
|
+
|
|
239
|
+
export function BulletList({ items }: { items: ReactNode[] }) {
|
|
240
|
+
return (
|
|
241
|
+
<ul
|
|
242
|
+
style={{
|
|
243
|
+
marginBottom: "var(--space-4)",
|
|
244
|
+
paddingLeft: "var(--space-5)",
|
|
245
|
+
}}
|
|
246
|
+
>
|
|
247
|
+
{items.map((item, i) => (
|
|
248
|
+
<li
|
|
249
|
+
key={i}
|
|
250
|
+
style={{
|
|
251
|
+
fontSize: "var(--text-subheadline)",
|
|
252
|
+
lineHeight: "var(--leading-relaxed)",
|
|
253
|
+
color: "var(--text-secondary)",
|
|
254
|
+
marginBottom: "var(--space-1)",
|
|
255
|
+
}}
|
|
256
|
+
>
|
|
257
|
+
{item}
|
|
258
|
+
</li>
|
|
259
|
+
))}
|
|
260
|
+
</ul>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function NumberedList({ items }: { items: ReactNode[] }) {
|
|
265
|
+
return (
|
|
266
|
+
<ol
|
|
267
|
+
style={{
|
|
268
|
+
marginBottom: "var(--space-4)",
|
|
269
|
+
paddingLeft: "var(--space-5)",
|
|
270
|
+
}}
|
|
271
|
+
>
|
|
272
|
+
{items.map((item, i) => (
|
|
273
|
+
<li
|
|
274
|
+
key={i}
|
|
275
|
+
style={{
|
|
276
|
+
fontSize: "var(--text-subheadline)",
|
|
277
|
+
lineHeight: "var(--leading-relaxed)",
|
|
278
|
+
color: "var(--text-secondary)",
|
|
279
|
+
marginBottom: "var(--space-1)",
|
|
280
|
+
}}
|
|
281
|
+
>
|
|
282
|
+
{item}
|
|
283
|
+
</li>
|
|
284
|
+
))}
|
|
285
|
+
</ol>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* ─── Callout ──────────────────────────────────────────────────── */
|
|
290
|
+
|
|
291
|
+
const CALLOUT_COLORS: Record<string, string> = {
|
|
292
|
+
tip: "var(--system-green)",
|
|
293
|
+
warning: "var(--system-orange)",
|
|
294
|
+
note: "var(--system-blue)",
|
|
295
|
+
error: "var(--system-red)",
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const CALLOUT_LABELS: Record<string, string> = {
|
|
299
|
+
tip: "Tip",
|
|
300
|
+
warning: "Warning",
|
|
301
|
+
note: "Note",
|
|
302
|
+
error: "Error",
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export function Callout({
|
|
306
|
+
type = "note",
|
|
307
|
+
children,
|
|
308
|
+
}: {
|
|
309
|
+
type?: "tip" | "warning" | "note" | "error";
|
|
310
|
+
children: ReactNode;
|
|
311
|
+
}) {
|
|
312
|
+
const color = CALLOUT_COLORS[type] ?? "var(--system-blue)";
|
|
313
|
+
return (
|
|
314
|
+
<div
|
|
315
|
+
style={{
|
|
316
|
+
borderLeft: `3px solid ${color}`,
|
|
317
|
+
background: "var(--material-thin)",
|
|
318
|
+
borderRadius: "0 var(--radius-sm) var(--radius-sm) 0",
|
|
319
|
+
padding: "var(--space-3) var(--space-4)",
|
|
320
|
+
marginBottom: "var(--space-4)",
|
|
321
|
+
}}
|
|
322
|
+
>
|
|
323
|
+
<div
|
|
324
|
+
style={{
|
|
325
|
+
fontSize: "var(--text-caption1)",
|
|
326
|
+
fontWeight: "var(--weight-semibold)",
|
|
327
|
+
color,
|
|
328
|
+
textTransform: "uppercase",
|
|
329
|
+
letterSpacing: "0.04em",
|
|
330
|
+
marginBottom: "var(--space-1)",
|
|
331
|
+
}}
|
|
332
|
+
>
|
|
333
|
+
{CALLOUT_LABELS[type] ?? "Note"}
|
|
334
|
+
</div>
|
|
335
|
+
<div
|
|
336
|
+
style={{
|
|
337
|
+
fontSize: "var(--text-footnote)",
|
|
338
|
+
lineHeight: "var(--leading-relaxed)",
|
|
339
|
+
color: "var(--text-secondary)",
|
|
340
|
+
}}
|
|
341
|
+
>
|
|
342
|
+
{children}
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Heading,
|
|
3
|
+
SubHeading,
|
|
4
|
+
Paragraph,
|
|
5
|
+
CodeBlock,
|
|
6
|
+
InlineCode,
|
|
7
|
+
Table,
|
|
8
|
+
BulletList,
|
|
9
|
+
NumberedList,
|
|
10
|
+
Callout,
|
|
11
|
+
} from "./DocSection";
|
|
12
|
+
|
|
13
|
+
export function GettingStartedSection() {
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
<Heading>Getting Started</Heading>
|
|
17
|
+
<Paragraph>
|
|
18
|
+
This guide walks you through getting ClawPort running against your own
|
|
19
|
+
OpenClaw instance. ClawPort is a Next.js 16 dashboard for managing,
|
|
20
|
+
monitoring, and talking directly to your OpenClaw AI agents.
|
|
21
|
+
</Paragraph>
|
|
22
|
+
|
|
23
|
+
<SubHeading>Prerequisites</SubHeading>
|
|
24
|
+
<BulletList
|
|
25
|
+
items={[
|
|
26
|
+
<>
|
|
27
|
+
<strong style={{ color: "var(--text-primary)" }}>Node.js 22+</strong>{" "}
|
|
28
|
+
-- verify with <InlineCode>node -v</InlineCode>
|
|
29
|
+
</>,
|
|
30
|
+
<>
|
|
31
|
+
<strong style={{ color: "var(--text-primary)" }}>OpenClaw</strong>{" "}
|
|
32
|
+
-- installed and working: <InlineCode>openclaw --version</InlineCode>
|
|
33
|
+
</>,
|
|
34
|
+
<>
|
|
35
|
+
<strong style={{ color: "var(--text-primary)" }}>
|
|
36
|
+
OpenClaw gateway running
|
|
37
|
+
</strong>{" "}
|
|
38
|
+
-- ClawPort talks to the gateway at{" "}
|
|
39
|
+
<InlineCode>localhost:18789</InlineCode>
|
|
40
|
+
</>,
|
|
41
|
+
]}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<SubHeading>Quick Start</SubHeading>
|
|
45
|
+
<CodeBlock title="terminal">
|
|
46
|
+
{`# Clone the repo
|
|
47
|
+
git clone https://github.com/openclaw/clawport.git
|
|
48
|
+
cd clawport
|
|
49
|
+
|
|
50
|
+
# Install dependencies
|
|
51
|
+
npm install
|
|
52
|
+
|
|
53
|
+
# Auto-detect your OpenClaw config and write .env.local
|
|
54
|
+
npm run setup
|
|
55
|
+
|
|
56
|
+
# Start the dev server
|
|
57
|
+
npm run dev`}
|
|
58
|
+
</CodeBlock>
|
|
59
|
+
<Paragraph>
|
|
60
|
+
Open <InlineCode>http://localhost:3000</InlineCode>. On first launch
|
|
61
|
+
you'll see the onboarding wizard which walks you through naming your
|
|
62
|
+
portal, choosing a theme, and personalizing agent avatars.
|
|
63
|
+
</Paragraph>
|
|
64
|
+
|
|
65
|
+
<SubHeading>Environment Variables</SubHeading>
|
|
66
|
+
<Paragraph>
|
|
67
|
+
The fastest way to configure is the auto-setup script:{" "}
|
|
68
|
+
<InlineCode>npm run setup</InlineCode>. It auto-detects your{" "}
|
|
69
|
+
<InlineCode>WORKSPACE_PATH</InlineCode>,{" "}
|
|
70
|
+
<InlineCode>OPENCLAW_BIN</InlineCode>, and gateway token from your local
|
|
71
|
+
OpenClaw installation.
|
|
72
|
+
</Paragraph>
|
|
73
|
+
<Paragraph>
|
|
74
|
+
To configure manually, copy the template and edit:
|
|
75
|
+
</Paragraph>
|
|
76
|
+
<CodeBlock>{`cp .env.example .env.local`}</CodeBlock>
|
|
77
|
+
|
|
78
|
+
<Table
|
|
79
|
+
headers={["Variable", "Required", "Description"]}
|
|
80
|
+
rows={[
|
|
81
|
+
[
|
|
82
|
+
<InlineCode key="ws">WORKSPACE_PATH</InlineCode>,
|
|
83
|
+
"Yes",
|
|
84
|
+
"Path to your OpenClaw workspace directory (default: ~/.openclaw/workspace)",
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
<InlineCode key="bin">OPENCLAW_BIN</InlineCode>,
|
|
88
|
+
"Yes",
|
|
89
|
+
"Absolute path to the openclaw CLI binary",
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
<InlineCode key="tok">OPENCLAW_GATEWAY_TOKEN</InlineCode>,
|
|
93
|
+
"Yes",
|
|
94
|
+
"Token that authenticates all API calls to the gateway",
|
|
95
|
+
],
|
|
96
|
+
[
|
|
97
|
+
<InlineCode key="el">ELEVENLABS_API_KEY</InlineCode>,
|
|
98
|
+
"No",
|
|
99
|
+
"ElevenLabs API key for voice/TTS indicators on agent profiles",
|
|
100
|
+
],
|
|
101
|
+
]}
|
|
102
|
+
/>
|
|
103
|
+
|
|
104
|
+
<Callout type="tip">
|
|
105
|
+
No separate AI API keys are needed. All AI calls (chat, vision, TTS,
|
|
106
|
+
transcription) route through the OpenClaw gateway. One subscription, one
|
|
107
|
+
token.
|
|
108
|
+
</Callout>
|
|
109
|
+
|
|
110
|
+
<SubHeading>Finding Your Values</SubHeading>
|
|
111
|
+
<NumberedList
|
|
112
|
+
items={[
|
|
113
|
+
<>
|
|
114
|
+
<strong style={{ color: "var(--text-primary)" }}>WORKSPACE_PATH</strong>:{" "}
|
|
115
|
+
Run <InlineCode>ls ~/.openclaw/workspace</InlineCode> to verify.
|
|
116
|
+
You should see files like <InlineCode>SOUL.md</InlineCode>, an{" "}
|
|
117
|
+
<InlineCode>agents/</InlineCode> directory, and a{" "}
|
|
118
|
+
<InlineCode>memory/</InlineCode> directory.
|
|
119
|
+
</>,
|
|
120
|
+
<>
|
|
121
|
+
<strong style={{ color: "var(--text-primary)" }}>OPENCLAW_BIN</strong>:{" "}
|
|
122
|
+
Run <InlineCode>which openclaw</InlineCode> and use the full path.
|
|
123
|
+
</>,
|
|
124
|
+
<>
|
|
125
|
+
<strong style={{ color: "var(--text-primary)" }}>
|
|
126
|
+
OPENCLAW_GATEWAY_TOKEN
|
|
127
|
+
</strong>
|
|
128
|
+
: Run <InlineCode>openclaw gateway status</InlineCode> to view your
|
|
129
|
+
gateway configuration including the token.
|
|
130
|
+
</>,
|
|
131
|
+
]}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
<SubHeading>Start the Gateway</SubHeading>
|
|
135
|
+
<Paragraph>
|
|
136
|
+
ClawPort expects the OpenClaw gateway running at{" "}
|
|
137
|
+
<InlineCode>localhost:18789</InlineCode>. Start it in a separate terminal:
|
|
138
|
+
</Paragraph>
|
|
139
|
+
<CodeBlock>{`openclaw gateway run`}</CodeBlock>
|
|
140
|
+
|
|
141
|
+
<SubHeading>First-Run Onboarding</SubHeading>
|
|
142
|
+
<Paragraph>
|
|
143
|
+
On your first visit, ClawPort launches the onboarding wizard (5 steps):
|
|
144
|
+
</Paragraph>
|
|
145
|
+
<NumberedList
|
|
146
|
+
items={[
|
|
147
|
+
"Naming your portal -- give your command centre a custom name and subtitle",
|
|
148
|
+
"Choosing a theme -- pick from Dark, Glass, Color, Light, or System",
|
|
149
|
+
"Setting an accent color -- personalize the UI highlight color",
|
|
150
|
+
"Voice chat -- optional microphone permission test",
|
|
151
|
+
"Overview -- feature summary of all pages",
|
|
152
|
+
]}
|
|
153
|
+
/>
|
|
154
|
+
<Paragraph>
|
|
155
|
+
All of these can be changed later in the Settings page.
|
|
156
|
+
</Paragraph>
|
|
157
|
+
|
|
158
|
+
<SubHeading>Production Build</SubHeading>
|
|
159
|
+
<CodeBlock title="terminal">
|
|
160
|
+
{`npx next build
|
|
161
|
+
npm start`}
|
|
162
|
+
</CodeBlock>
|
|
163
|
+
<Paragraph>
|
|
164
|
+
The production server runs on port 3000 by default. The gateway still
|
|
165
|
+
needs to be running.
|
|
166
|
+
</Paragraph>
|
|
167
|
+
</>
|
|
168
|
+
);
|
|
169
|
+
}
|