clawport-ui 0.2.4 → 0.3.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 CHANGED
@@ -16,8 +16,11 @@ ClawPort is an open-source dashboard for managing, monitoring, and talking direc
16
16
 
17
17
  ### Quick Start (npm)
18
18
 
19
+ > **Note:** The npm package is `clawport-ui`. The CLI command is `clawport`.
20
+ > Do not install the unrelated `clawport` package.
21
+
19
22
  ```bash
20
- # Install globally
23
+ # Install globally (package: clawport-ui, command: clawport)
21
24
  npm install -g clawport-ui
22
25
 
23
26
  # Auto-detect your OpenClaw config
@@ -225,7 +228,7 @@ Five built-in themes, toggled via the sidebar button:
225
228
 
226
229
  | Theme | Description |
227
230
  |-------|-------------|
228
- | **Dark** | Apple Dark Mode with warm blacks, gold accent |
231
+ | **Dark** | Apple Dark Mode with warm blacks, red accent |
229
232
  | **Glass** | Frosted translucent panels on deep blue-black |
230
233
  | **Color** | Vibrant purple-indigo gradients |
231
234
  | **Light** | Apple Light Mode, clean whites |
@@ -274,7 +277,7 @@ npm install -g clawport-ui
274
277
  clawport help
275
278
  ```
276
279
 
277
- Published as [`clawport-ui`](https://www.npmjs.com/package/clawport-ui) on npm.
280
+ Published as [`clawport-ui`](https://www.npmjs.com/package/clawport-ui) on npm. The CLI command is `clawport` (not `clawport-ui`). The separate `clawport` npm package is unrelated and not affiliated with this project.
278
281
 
279
282
  ### CLI Commands
280
283
 
package/app/globals.css CHANGED
@@ -80,9 +80,9 @@
80
80
  --text-secondary: rgba(235,235,245,0.60);
81
81
  --text-tertiary: rgba(235,235,245,0.30);
82
82
  --text-quaternary: rgba(235,235,245,0.18);
83
- --accent: #F5C518;
84
- --accent-fill: rgba(245,197,24,0.15);
85
- --accent-contrast: #000;
83
+ --accent: #EF4444;
84
+ --accent-fill: rgba(239,68,68,0.15);
85
+ --accent-contrast: #fff;
86
86
  --system-blue: #0A84FF;
87
87
  --system-green: #30D158;
88
88
  --system-red: #FF453A;
@@ -128,9 +128,9 @@
128
128
  --text-secondary: rgba(255,255,255,0.60);
129
129
  --text-tertiary: rgba(255,255,255,0.30);
130
130
  --text-quaternary: rgba(255,255,255,0.18);
131
- --accent: #F5C518;
132
- --accent-fill: rgba(245,197,24,0.18);
133
- --accent-contrast: #000;
131
+ --accent: #EF4444;
132
+ --accent-fill: rgba(239,68,68,0.18);
133
+ --accent-contrast: #fff;
134
134
  --system-blue: #3B9EFF;
135
135
  --system-green: #34D058;
136
136
  --system-red: #FF5C57;
@@ -176,9 +176,9 @@
176
176
  --text-secondary: rgba(220,210,255,0.70);
177
177
  --text-tertiary: rgba(220,210,255,0.35);
178
178
  --text-quaternary: rgba(220,210,255,0.20);
179
- --accent: #F5C518;
180
- --accent-fill: rgba(245,197,24,0.20);
181
- --accent-contrast: #000;
179
+ --accent: #EF4444;
180
+ --accent-fill: rgba(239,68,68,0.20);
181
+ --accent-contrast: #fff;
182
182
  --system-blue: #60A5FA;
183
183
  --system-green: #34D399;
184
184
  --system-red: #F87171;
@@ -224,8 +224,8 @@
224
224
  --text-secondary: rgba(60,60,67,0.60);
225
225
  --text-tertiary: rgba(60,60,67,0.44);
226
226
  --text-quaternary: rgba(60,60,67,0.30);
227
- --accent: #B8860B;
228
- --accent-fill: rgba(184,134,11,0.12);
227
+ --accent: #DC2626;
228
+ --accent-fill: rgba(220,38,38,0.12);
229
229
  --accent-contrast: #fff;
230
230
  --system-blue: #007AFF;
231
231
  --system-green: #28CD41;
@@ -12,10 +12,10 @@ import { OnboardingWizard } from '@/components/OnboardingWizard'
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
14
  const ACCENT_PRESETS = [
15
+ { label: 'Red', value: '#EF4444' },
15
16
  { label: 'Gold', value: '#F5C518' },
16
17
  { label: 'Blue', value: '#3B82F6' },
17
18
  { label: 'Green', value: '#22C55E' },
18
- { label: 'Red', value: '#EF4444' },
19
19
  { label: 'Orange', value: '#F97316' },
20
20
  { label: 'Purple', value: '#A855F7' },
21
21
  { label: 'Pink', value: '#EC4899' },
@@ -368,7 +368,7 @@ export default function SettingsPage() {
368
368
  ? 'transparent'
369
369
  : settings.accentColor
370
370
  ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
371
- : 'linear-gradient(135deg, #f5c518, #e8b800)',
371
+ : 'transparent',
372
372
  boxShadow: settings.iconBgHidden ? 'none' : 'var(--shadow-card)',
373
373
  display: 'flex',
374
374
  alignItems: 'center',
package/bin/clawport.mjs CHANGED
@@ -3,8 +3,21 @@
3
3
  import { fileURLToPath } from 'node:url'
4
4
  import { dirname, resolve } from 'node:path'
5
5
  import { spawn } from 'node:child_process'
6
- import { existsSync, readFileSync } from 'node:fs'
6
+ import { existsSync, readFileSync, accessSync, constants } from 'node:fs'
7
7
  import { execSync } from 'node:child_process'
8
+ import { createServer } from 'node:net'
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Node version gate (must be 22+ for native fetch / AbortSignal.timeout)
12
+ // ---------------------------------------------------------------------------
13
+
14
+ const [major] = process.versions.node.split('.').map(Number)
15
+ if (major < 22) {
16
+ console.error(
17
+ `\x1b[31mError:\x1b[0m Node.js 22+ required (found ${process.versions.node})`
18
+ )
19
+ process.exit(1)
20
+ }
8
21
 
9
22
  // ---------------------------------------------------------------------------
10
23
  // Resolve package root (where app/, lib/, etc. live)
@@ -25,6 +38,8 @@ const red = (s) => `\x1b[31m${s}\x1b[0m`
25
38
  const dim = (s) => `\x1b[2m${s}\x1b[0m`
26
39
  const bold = (s) => `\x1b[1m${s}\x1b[0m`
27
40
 
41
+ const extraArgs = process.argv.slice(3)
42
+
28
43
  function run(cmd, args = []) {
29
44
  const child = spawn(cmd, args, {
30
45
  cwd: PKG_ROOT,
@@ -34,6 +49,40 @@ function run(cmd, args = []) {
34
49
  child.on('close', (code) => process.exit(code ?? 0))
35
50
  }
36
51
 
52
+ async function checkGateway() {
53
+ try {
54
+ const res = await fetch('http://127.0.0.1:18789/', {
55
+ signal: AbortSignal.timeout(3000),
56
+ })
57
+ return res.ok || res.status > 0
58
+ } catch {
59
+ return false
60
+ }
61
+ }
62
+
63
+ function findBinary(name) {
64
+ const cmd = process.platform === 'win32' ? 'where' : 'which'
65
+ try {
66
+ return execSync(`${cmd} ${name}`, {
67
+ encoding: 'utf-8',
68
+ stdio: ['pipe', 'pipe', 'pipe'],
69
+ }).trim()
70
+ } catch {
71
+ return null
72
+ }
73
+ }
74
+
75
+ function checkPort(port) {
76
+ return new Promise((resolve) => {
77
+ const server = createServer()
78
+ server.once('error', () => resolve(false))
79
+ server.once('listening', () => {
80
+ server.close(() => resolve(true))
81
+ })
82
+ server.listen(port, '127.0.0.1')
83
+ })
84
+ }
85
+
37
86
  // ---------------------------------------------------------------------------
38
87
  // Commands
39
88
  // ---------------------------------------------------------------------------
@@ -42,19 +91,25 @@ function showHelp() {
42
91
  console.log(`
43
92
  ${bold('ClawPort')} -- AI Agent Dashboard
44
93
 
45
- ${bold('Usage:')} clawport <command>
94
+ ${bold('Usage:')} clawport <command> [options]
46
95
 
47
96
  ${bold('Commands:')}
48
97
  ${green('dev')} Start the development server (next dev)
49
98
  ${green('start')} Build and start the production server
50
99
  ${green('setup')} Run the setup wizard (auto-detect OpenClaw config)
51
100
  ${green('status')} Check gateway reachability and current config
101
+ ${green('doctor')} Run full environment health check
52
102
  ${green('help')} Show this help message
53
103
 
104
+ ${bold('Options:')}
105
+ ${dim('--port <n>')} Port for dev/start (passed through to Next.js)
106
+
54
107
  ${bold('Examples:')}
55
- ${dim('$ clawport setup # Configure your OpenClaw connection')}
56
- ${dim('$ clawport dev # Start dev server on localhost:3000')}
57
- ${dim('$ clawport status # Check if gateway is reachable')}
108
+ ${dim('$ clawport setup # Configure your OpenClaw connection')}
109
+ ${dim('$ clawport dev # Start dev server on localhost:3000')}
110
+ ${dim('$ clawport dev --port 3005 # Start dev server on port 3005')}
111
+ ${dim('$ clawport status # Check if gateway is reachable')}
112
+ ${dim('$ clawport doctor # Diagnose environment issues')}
58
113
 
59
114
  ${dim(`Package root: ${PKG_ROOT}`)}
60
115
  `)
@@ -62,12 +117,20 @@ ${dim(`Package root: ${PKG_ROOT}`)}
62
117
 
63
118
  function cmdDev() {
64
119
  console.log(`\n ${bold('Starting ClawPort dev server...')}\n`)
65
- run(NEXT_BIN, ['dev'])
120
+ run(NEXT_BIN, ['dev', ...extraArgs])
66
121
  }
67
122
 
68
123
  function cmdStart() {
69
124
  console.log(`\n ${bold('Building and starting ClawPort...')}\n`)
70
- run(NEXT_BIN, ['build', '&&', NEXT_BIN, 'start'])
125
+ const build = spawn(NEXT_BIN, ['build'], {
126
+ cwd: PKG_ROOT,
127
+ stdio: 'inherit',
128
+ shell: true,
129
+ })
130
+ build.on('close', (code) => {
131
+ if (code !== 0) process.exit(code)
132
+ run(NEXT_BIN, ['start', ...extraArgs])
133
+ })
71
134
  }
72
135
 
73
136
  function cmdSetup() {
@@ -75,22 +138,13 @@ function cmdSetup() {
75
138
  run('node', [resolve(PKG_ROOT, 'scripts/setup.mjs'), `--cwd=${PKG_ROOT}`])
76
139
  }
77
140
 
78
- function cmdStatus() {
141
+ async function cmdStatus() {
79
142
  console.log()
80
143
  console.log(bold(' ClawPort Status'))
81
144
  console.log()
82
145
 
83
146
  // Check gateway
84
- let gatewayUp = false
85
- try {
86
- const result = execSync(
87
- 'curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/ 2>/dev/null',
88
- { encoding: 'utf-8', timeout: 5000 }
89
- ).trim()
90
- gatewayUp = result && result !== '000'
91
- } catch {
92
- // gateway not reachable
93
- }
147
+ const gatewayUp = await checkGateway()
94
148
 
95
149
  if (gatewayUp) {
96
150
  console.log(` ${green('+')} Gateway reachable at ${dim('localhost:18789')}`)
@@ -125,6 +179,89 @@ function cmdStatus() {
125
179
  console.log()
126
180
  }
127
181
 
182
+ async function cmdDoctor() {
183
+ console.log()
184
+ console.log(bold(' ClawPort Doctor'))
185
+ console.log()
186
+
187
+ let passed = 0
188
+ let total = 0
189
+
190
+ function check(ok, label, fix) {
191
+ total++
192
+ if (ok) {
193
+ passed++
194
+ console.log(` ${green('+')} ${label}`)
195
+ } else {
196
+ console.log(` ${red('x')} ${label}`)
197
+ if (fix) console.log(` ${dim(fix)}`)
198
+ }
199
+ }
200
+
201
+ // 1. Node.js version
202
+ check(major >= 22, `Node.js ${process.versions.node}`, 'Upgrade to Node.js 22 or later')
203
+
204
+ // 2. Package integrity -- next binary exists
205
+ check(existsSync(NEXT_BIN), 'Package integrity (node_modules/.bin/next)', 'Run: npm install')
206
+
207
+ // 3. OpenClaw binary
208
+ const openclawPath = findBinary('openclaw')
209
+ check(!!openclawPath, openclawPath ? `OpenClaw binary (${openclawPath})` : 'OpenClaw binary', 'Install OpenClaw: https://docs.openclaw.dev/install')
210
+
211
+ // 4. Gateway reachable
212
+ const gatewayUp = await checkGateway()
213
+ check(gatewayUp, 'Gateway reachable at localhost:18789', 'Start it with: openclaw gateway run')
214
+
215
+ // 5. Configuration -- .env.local with required vars
216
+ const envPath = resolve(PKG_ROOT, '.env.local')
217
+ const requiredVars = ['WORKSPACE_PATH', 'OPENCLAW_BIN', 'OPENCLAW_GATEWAY_TOKEN']
218
+ let envOk = false
219
+ let envFix = 'Run: clawport setup'
220
+ if (existsSync(envPath)) {
221
+ const content = readFileSync(envPath, 'utf-8')
222
+ const missing = requiredVars.filter((v) => !content.includes(`${v}=`))
223
+ if (missing.length === 0) {
224
+ envOk = true
225
+ } else {
226
+ envFix = `Missing in .env.local: ${missing.join(', ')}`
227
+ }
228
+ }
229
+ check(envOk, '.env.local with required variables', envFix)
230
+
231
+ // 6. Workspace structure
232
+ let workspaceOk = false
233
+ let workspaceFix = 'Set WORKSPACE_PATH in .env.local via: clawport setup'
234
+ if (envOk) {
235
+ const content = readFileSync(envPath, 'utf-8')
236
+ const match = content.match(/^WORKSPACE_PATH=(.+)$/m)
237
+ if (match) {
238
+ const wsPath = match[1].trim()
239
+ const hasSoul = existsSync(resolve(wsPath, 'SOUL.md'))
240
+ const hasAgents = existsSync(resolve(wsPath, 'agents'))
241
+ const hasMemory = existsSync(resolve(wsPath, 'memory'))
242
+ if (hasSoul || hasAgents || hasMemory) {
243
+ workspaceOk = true
244
+ } else {
245
+ workspaceFix = `Workspace at ${wsPath} missing expected files (SOUL.md, agents/, memory/)`
246
+ }
247
+ }
248
+ }
249
+ check(workspaceOk, 'Workspace structure', workspaceFix)
250
+
251
+ // 7. Port 3000 available
252
+ const portFree = await checkPort(3000)
253
+ check(portFree, 'Port 3000 available', 'Port 3000 is in use. Use: clawport dev --port 3001')
254
+
255
+ // Summary
256
+ console.log()
257
+ if (passed === total) {
258
+ console.log(` ${green(`${passed}/${total} checks passed`)}`)
259
+ } else {
260
+ console.log(` ${yellow(`${passed}/${total} checks passed`)} -- ${total - passed} issue${total - passed === 1 ? '' : 's'} found`)
261
+ }
262
+ console.log()
263
+ }
264
+
128
265
  // ---------------------------------------------------------------------------
129
266
  // Main
130
267
  // ---------------------------------------------------------------------------
@@ -144,6 +281,9 @@ switch (command) {
144
281
  case 'status':
145
282
  cmdStatus()
146
283
  break
284
+ case 'doctor':
285
+ cmdDoctor()
286
+ break
147
287
  case 'help':
148
288
  default:
149
289
  showHelp()
@@ -116,7 +116,7 @@ export function MobileSidebar({
116
116
  borderRadius: '6px',
117
117
  background: settings.accentColor
118
118
  ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
119
- : 'linear-gradient(135deg, #f5c518, #e8b800)',
119
+ : 'transparent',
120
120
  display: 'flex',
121
121
  alignItems: 'center',
122
122
  justifyContent: 'center',
@@ -202,7 +202,7 @@ export function MobileSidebar({
202
202
  borderRadius: '10px',
203
203
  background: settings.accentColor
204
204
  ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
205
- : 'linear-gradient(135deg, #f5c518, #e8b800)',
205
+ : 'transparent',
206
206
  boxShadow: 'var(--shadow-card)',
207
207
  display: 'flex',
208
208
  alignItems: 'center',
@@ -12,10 +12,10 @@ import type { ThemeId } from '@/lib/themes'
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
14
  const ACCENT_PRESETS = [
15
+ { label: 'Red', value: '#EF4444' },
15
16
  { label: 'Gold', value: '#F5C518' },
16
17
  { label: 'Blue', value: '#3B82F6' },
17
18
  { label: 'Green', value: '#22C55E' },
18
- { label: 'Red', value: '#EF4444' },
19
19
  { label: 'Orange', value: '#F97316' },
20
20
  { label: 'Purple', value: '#A855F7' },
21
21
  { label: 'Pink', value: '#EC4899' },
@@ -508,7 +508,7 @@ export function OnboardingWizard({ forceOpen, onClose }: OnboardingWizardProps)
508
508
  padding: '1px 4px',
509
509
  borderRadius: 3,
510
510
  color: 'var(--code-text)',
511
- }}>npm run setup</code> in your terminal to auto-detect and configure your environment.
511
+ }}>clawport setup</code> in your terminal to auto-detect and configure your environment.
512
512
  You can continue setup and fix this later.
513
513
  </div>
514
514
  </div>
@@ -657,7 +657,7 @@ export function OnboardingWizard({ forceOpen, onClose }: OnboardingWizardProps)
657
657
  borderRadius: 8,
658
658
  background: settings.accentColor
659
659
  ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
660
- : 'linear-gradient(135deg, #f5c518, #e8b800)',
660
+ : 'transparent',
661
661
  display: 'flex',
662
662
  alignItems: 'center',
663
663
  justifyContent: 'center',
@@ -60,7 +60,7 @@ export function Sidebar() {
60
60
  ? 'transparent'
61
61
  : settings.accentColor
62
62
  ? `linear-gradient(135deg, ${settings.accentColor}, ${settings.accentColor}dd)`
63
- : 'linear-gradient(135deg, #f5c518, #e8b800)',
63
+ : 'transparent',
64
64
  boxShadow: settings.iconBgHidden ? 'none' : 'var(--shadow-card)',
65
65
  display: 'flex',
66
66
  alignItems: 'center',
@@ -42,8 +42,13 @@ export function GettingStartedSection() {
42
42
  />
43
43
 
44
44
  <SubHeading>Quick Start (npm)</SubHeading>
45
+ <Callout type="note">
46
+ The npm package is <InlineCode>clawport-ui</InlineCode>. The CLI command
47
+ is <InlineCode>clawport</InlineCode>. Do not install the unrelated{" "}
48
+ <InlineCode>clawport</InlineCode> package.
49
+ </Callout>
45
50
  <CodeBlock title="terminal">
46
- {`# Install globally
51
+ {`# Install globally (package: clawport-ui, command: clawport)
47
52
  npm install -g clawport-ui
48
53
 
49
54
  # Run the setup wizard (auto-detects your OpenClaw config)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawport-ui",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "Open-source dashboard for managing, monitoring, and chatting with your OpenClaw AI agents.",
5
5
  "homepage": "https://clawport.dev",
6
6
  "repository": {
@@ -19,6 +19,22 @@
19
19
  "next.js"
20
20
  ],
21
21
  "license": "MIT",
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "files": [
26
+ "app/",
27
+ "bin/",
28
+ "components/",
29
+ "docs/",
30
+ "lib/",
31
+ "public/",
32
+ "scripts/",
33
+ "next.config.mjs",
34
+ "postcss.config.mjs",
35
+ "tsconfig.json",
36
+ ".env.example"
37
+ ],
22
38
  "bin": {
23
39
  "clawport": "./bin/clawport.mjs"
24
40
  },
package/scripts/setup.mjs CHANGED
@@ -48,7 +48,8 @@ function detectWorkspacePath() {
48
48
  }
49
49
 
50
50
  function detectOpenClawBin() {
51
- return exec('which openclaw')
51
+ const cmd = process.platform === 'win32' ? 'where' : 'which'
52
+ return exec(`${cmd} openclaw`)
52
53
  }
53
54
 
54
55
  function detectGatewayToken() {
@@ -63,9 +64,15 @@ function detectGatewayToken() {
63
64
  }
64
65
  }
65
66
 
66
- function checkGatewayRunning() {
67
- const result = exec('curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:18789/ 2>/dev/null')
68
- return result && result !== '000'
67
+ async function checkGatewayRunning() {
68
+ try {
69
+ const res = await fetch('http://127.0.0.1:18789/', {
70
+ signal: AbortSignal.timeout(3000),
71
+ })
72
+ return res.ok || res.status > 0
73
+ } catch {
74
+ return false
75
+ }
69
76
  }
70
77
 
71
78
  // ---------------------------------------------------------------------------
@@ -84,7 +91,7 @@ async function main() {
84
91
  OPENCLAW_GATEWAY_TOKEN: detectGatewayToken(),
85
92
  }
86
93
 
87
- const gatewayUp = checkGatewayRunning()
94
+ const gatewayUp = await checkGatewayRunning()
88
95
 
89
96
  // Report findings
90
97
  const entries = [
@@ -203,12 +210,13 @@ async function main() {
203
210
  console.log()
204
211
  console.log(` ${green('Done!')} .env.local written.`)
205
212
  console.log()
213
+ const startCmd = cwdFlag ? 'clawport dev' : 'npm run dev'
206
214
  console.log(` Next steps:`)
207
215
  if (!gatewayUp) {
208
216
  console.log(` 1. Start the gateway: ${dim('openclaw gateway run')}`)
209
- console.log(` 2. Start ClawPort: ${dim('npm run dev')}`)
217
+ console.log(` 2. Start ClawPort: ${dim(startCmd)}`)
210
218
  } else {
211
- console.log(` ${dim('npm run dev')}`)
219
+ console.log(` ${dim(startCmd)}`)
212
220
  }
213
221
  console.log()
214
222
  }
package/BRANDING.md DELETED
@@ -1,132 +0,0 @@
1
- # Branding Reference
2
-
3
- Current brand name: **ClawPort**
4
-
5
- This document maps every location in the codebase where the brand name appears. Use it as a checklist when rebranding.
6
-
7
- ---
8
-
9
- ## 1. User-Facing Text (UI strings, page titles)
10
-
11
- These are what end users see. Change these first during a rebrand.
12
-
13
- | File | Line(s) | What | Current Value |
14
- |------|---------|------|---------------|
15
- | `app/layout.tsx` | ~10 | `<title>` metadata | `"ClawPort -- Command Centre"` |
16
- | `components/Sidebar.tsx` | ~84 | Sidebar header fallback name | `'ClawPort'` |
17
- | `components/MobileSidebar.tsx` | ~138, ~226 | Mobile sidebar fallback name | `'ClawPort'` |
18
- | `components/OnboardingWizard.tsx` | ~271 | Welcome screen heading | `"Welcome to ClawPort"` |
19
- | `components/OnboardingWizard.tsx` | ~282 | Welcome screen description | `"A visual command centre for your AI agent team..."` |
20
- | `components/OnboardingWizard.tsx` | ~572 | Name input placeholder | `"ClawPort"` |
21
- | `components/OnboardingWizard.tsx` | ~675 | Sidebar preview fallback | `'ClawPort'` |
22
- | `components/GlobalSearch.tsx` | ~349, ~389-390 | Search modal aria-label + placeholder | `"Search ClawPort"` / `"Search ClawPort..."` |
23
- | `app/chat/page.tsx` | ~173 | Chat page header | `"ClawPort Messages"` |
24
- | `lib/agents.json` | ~5, ~14 | Default root agent title + description | `"ClawPort Orchestrator"` |
25
- | `scripts/setup.mjs` | ~77 | Setup script banner | `"ClawPort Setup"` |
26
- | `scripts/setup.mjs` | ~184 | Generated .env.local comment | `"# ClawPort -- generated by npm run setup"` |
27
- | `scripts/setup.mjs` | ~205 | Post-setup instructions | `"Start ClawPort"` |
28
- | `.env.example` | ~2 | File header comment | `"# ClawPort -- Environment Configuration"` |
29
-
30
- ## 2. Internal Code Identifiers
31
-
32
- TypeScript interfaces, function names, component names, and variable names. These are developer-facing only. Renaming requires updating all imports and references.
33
-
34
- | Identifier | Files | Notes |
35
- |------------|-------|-------|
36
- | `ClawPortSettings` (interface) | `lib/settings.ts`, `app/settings-provider.tsx`, `docs/THEMING.md`, `docs/COMPONENTS.md` | Core settings type. 20+ references. |
37
- | `portalName` (field) | `lib/settings.ts`, `app/settings-provider.tsx`, `components/Sidebar.tsx`, `MobileSidebar.tsx`, `OnboardingWizard.tsx`, `app/settings/page.tsx`, `DynamicFavicon.tsx` | Settings field for custom name. |
38
- | `portalSubtitle` (field) | Same as `portalName` | Settings field for subtitle. |
39
- | `portalEmoji` (field) | Same as `portalName` | Settings field for emoji. |
40
- | `portalIcon` (field) | Same as `portalName` | Settings field for uploaded icon image. |
41
- | `setPortalName` (setter) | `app/settings-provider.tsx`, `OnboardingWizard.tsx`, `app/settings/page.tsx` | Context setter callback. |
42
- | `setPortalSubtitle` (setter) | Same as `setPortalName` | |
43
- | `setPortalEmoji` (setter) | Same as `setPortalName` | |
44
- | `setPortalIcon` (setter) | Same as `setPortalName` | |
45
- | `OrgMap` (component) | `components/OrgMap.tsx`, `app/page.tsx`, `docs/COMPONENTS.md` | React Flow org chart component. |
46
- | `OrgMapProps` (interface) | `components/OrgMap.tsx` | Props type for OrgMap. |
47
- | `HomePage` (component) | `app/page.tsx` | Home page component. |
48
- | `handleIconUpload` (function) | `app/settings/page.tsx` | Icon upload handler. |
49
- | `iconInputRef` (ref) | `app/settings/page.tsx` | File input ref. |
50
-
51
- ## 3. localStorage Keys
52
-
53
- Changing these breaks existing users' saved data. Requires a migration function or accept data loss.
54
-
55
- | Key | File(s) | Purpose |
56
- |-----|---------|---------|
57
- | `clawport-settings` | `lib/settings.ts` | All user settings (name, subtitle, emoji, icon, accent, etc.) |
58
- | `clawport-theme` | `app/providers.tsx` | Selected theme ID |
59
- | `clawport-onboarded` | `components/OnboardingWizard.tsx` | First-run completion flag |
60
- | `clawport-conversations` | `lib/conversations.ts` | Chat message history per agent |
61
- | `clawport-kanban` | `lib/kanban/store.ts` | Kanban board state |
62
-
63
- ## 4. Custom Events
64
-
65
- | Event Name | File(s) | Purpose |
66
- |------------|---------|---------|
67
- | `clawport:open-search` | `components/Sidebar.tsx`, `components/GlobalSearch.tsx` | Triggers Cmd+K search modal |
68
-
69
- ## 5. API / Session Keys
70
-
71
- | Key | File | Purpose |
72
- |-----|------|---------|
73
- | `agent:main:clawport` | `lib/anthropic.ts` | Default session key for OpenClaw gateway calls |
74
- | `clawport-${Date.now()}-...` | `lib/anthropic.ts` | Idempotency key prefix for chat.send |
75
-
76
- ## 6. Package / Repository
77
-
78
- | Location | Current Value |
79
- |----------|---------------|
80
- | `package.json` `name` | `clawport-ui` |
81
- | `package-lock.json` `name` | `clawport-ui` |
82
- | npm package | [`clawport-ui`](https://www.npmjs.com/package/clawport-ui) |
83
- | Git clone URLs in docs | `https://github.com/openclaw/clawport.git` |
84
-
85
- ## 7. Workspace Paths
86
-
87
- The user's agent registry override lives at `$WORKSPACE_PATH/clawport/agents.json`. This path is referenced in:
88
-
89
- | File | What |
90
- |------|------|
91
- | `lib/agents-registry.ts` | `join(workspacePath, 'clawport', 'agents.json')` |
92
- | Multiple test files | Mock paths like `/tmp/test-workspace/clawport/agents.json` |
93
- | Documentation | Setup instructions referencing `$WORKSPACE_PATH/clawport/` |
94
-
95
- ## 8. Documentation Files
96
-
97
- Every `.md` file contains brand references. Full list:
98
-
99
- | File | Approximate Occurrences |
100
- |------|------------------------|
101
- | `README.md` | ~20 |
102
- | `SETUP.md` | ~25 |
103
- | `CLAUDE.md` | ~20 |
104
- | `docs/API.md` | ~5 |
105
- | `docs/THEMING.md` | ~30 |
106
- | `docs/COMPONENTS.md` | ~35 |
107
- | `.env.example` | 1 |
108
-
109
- ## 9. Test Files
110
-
111
- Tests reference brand strings in mock data and localStorage keys:
112
-
113
- | File | What |
114
- |------|------|
115
- | `lib/settings.test.ts` | `clawport-settings` key, `portalName`/`portalSubtitle`/`portalEmoji`/`portalIcon` fields |
116
- | `lib/agents.test.ts` | `clawport/agents.json` paths, "ClawPort Orchestrator" in mock data |
117
- | `lib/conversations.test.ts` | `clawport-conversations` key |
118
- | `lib/kanban/store.test.ts` | `clawport-kanban` key |
119
-
120
- ---
121
-
122
- ## Rebrand Checklist
123
-
124
- 1. **User-facing text** (Section 1) -- find-and-replace the display name
125
- 2. **Documentation** (Section 8) -- update all .md files
126
- 3. **Package name** (Section 6) -- update package.json, re-run npm install
127
- 4. **Internal identifiers** (Section 2) -- rename interfaces, components, functions, fields
128
- 5. **localStorage keys** (Section 3) -- add migration in `loadSettings()` to read old keys
129
- 6. **Custom events** (Section 4) -- rename event strings
130
- 7. **API keys** (Section 5) -- update session key and idempotency prefix
131
- 8. **Workspace paths** (Section 7) -- update `agents-registry.ts` path, support both old/new
132
- 9. **Test files** (Section 9) -- update all mock data and key references
package/CLAUDE.md DELETED
@@ -1,266 +0,0 @@
1
- # ClawPort -- Developer Guide
2
-
3
- ## Quick Reference
4
-
5
- ```bash
6
- npm run setup # Auto-detect OpenClaw config, write .env.local
7
- npm run dev # Start dev server (Turbopack, port 3000)
8
- npm test # Run all 288 tests via Vitest (17 suites)
9
- npx tsc --noEmit # Type-check (expect 0 errors)
10
- npx next build # Production build
11
- ```
12
-
13
- ### CLI (global install)
14
-
15
- ```bash
16
- npm install -g clawport-ui
17
- clawport setup # Auto-detect config, write .env.local into package dir
18
- clawport dev # Start dev server
19
- clawport start # Build + start production server
20
- clawport status # Check gateway reachability + env config
21
- clawport help # Show usage
22
- ```
23
-
24
- The CLI resolves its own package root via `import.meta.url`, so all commands work regardless of the user's current working directory. Entry point: `bin/clawport.mjs`.
25
-
26
- ## Project Overview
27
-
28
- ClawPort is a Next.js 16 dashboard for managing OpenClaw AI agents. It provides an org chart (Org Map), direct agent chat with multimodal support, cron monitoring, and memory browsing. All AI calls route through the OpenClaw gateway -- no separate API keys needed.
29
-
30
- ## Tech Stack
31
-
32
- - Next.js 16.1.6 (App Router, Turbopack)
33
- - React 19.2.3, TypeScript 5
34
- - Tailwind CSS 4 with CSS custom properties for theming
35
- - Vitest 4 with jsdom environment
36
- - OpenAI SDK (routed to Claude via OpenClaw gateway at localhost:18789)
37
- - React Flow (@xyflow/react) for org chart
38
-
39
- ## Environment Variables
40
-
41
- ```env
42
- WORKSPACE_PATH # Required -- path to .openclaw/workspace
43
- OPENCLAW_BIN # Required -- path to openclaw binary
44
- OPENCLAW_GATEWAY_TOKEN # Required -- gateway auth token
45
- ELEVENLABS_API_KEY # Optional -- voice indicators
46
- ```
47
-
48
- Run `npm run setup` to auto-detect all required values from your local OpenClaw installation.
49
-
50
- ## Architecture
51
-
52
- ### Agent Registry Resolution
53
-
54
- ```
55
- loadRegistry() checks:
56
- 1. $WORKSPACE_PATH/clawport/agents.json (user override)
57
- 2. Bundled lib/agents.json (default)
58
- ```
59
-
60
- `lib/agents-registry.ts` exports `loadRegistry()`. `lib/agents.ts` calls it to build the full agent list (merging in SOUL.md content from the workspace). Users customize their agent team by dropping an `agents.json` into their workspace -- no source edits needed.
61
-
62
- ### operatorName Flow
63
-
64
- ```
65
- OnboardingWizard / Settings page
66
- -> ClawPortSettings.operatorName (localStorage)
67
- -> settings-provider.tsx (React context)
68
- -> NavLinks.tsx (dynamic initials + display name)
69
- -> ConversationView.tsx (sends operatorName in POST body)
70
- -> /api/chat/[id] route (injects into system prompt: "You are speaking with {operatorName}")
71
- ```
72
-
73
- No hardcoded operator names anywhere. Falls back to "Operator" / "??" when unset.
74
-
75
- ### Chat Pipeline (Text)
76
-
77
- ```
78
- Client -> POST /api/chat/[id] -> OpenAI SDK -> localhost:18789/v1/chat/completions -> Claude
79
- (streaming SSE response)
80
- ```
81
-
82
- ### Chat Pipeline (Images/Vision)
83
-
84
- The gateway's HTTP endpoint strips image_url content. Vision uses the CLI agent pipeline:
85
-
86
- ```
87
- Client resizes image to 1200px max (Canvas API)
88
- -> base64 data URL in message
89
- -> POST /api/chat/[id]
90
- -> Detects image in LATEST user message only (not history)
91
- -> execFile: openclaw gateway call chat.send --params <json> --token <token>
92
- -> Polls: openclaw gateway call chat.history every 2s
93
- -> Matches response by timestamp >= sendTs
94
- -> Returns assistant text via SSE
95
- ```
96
-
97
- Key files: `lib/anthropic.ts` (send + poll logic), `app/api/chat/[id]/route.ts` (routing)
98
-
99
- **Why send-then-poll?** `chat.send` is async -- it returns `{runId, status: "started"}` immediately. The `--expect-final` flag doesn't block for this method. We poll `chat.history` until the assistant's response appears.
100
-
101
- **Why CLI and not WebSocket?** The gateway WebSocket requires device keypair signing for `operator.write` scope (needed by `chat.send`). The CLI has the device keys; custom clients don't.
102
-
103
- **Why resize to 1200px?** macOS ARG_MAX is 1MB. Unresized photos can produce multi-MB base64 that exceeds CLI argument limits (E2BIG error). 1200px JPEG at 0.85 quality keeps base64 well under 1MB.
104
-
105
- ### Voice Message Pipeline
106
-
107
- ```
108
- Browser MediaRecorder (webm/opus or mp4)
109
- -> AudioContext AnalyserNode captures waveform (40-60 samples)
110
- -> Stop -> audioBlob + waveform data
111
- -> POST /api/transcribe (Whisper via gateway)
112
- -> Transcription text sent as message content
113
- -> Audio data URL + waveform stored in message for playback
114
- ```
115
-
116
- Key files: `lib/audio-recorder.ts`, `lib/transcribe.ts`, `components/chat/VoiceMessage.tsx`
117
-
118
- ### Conversation Persistence
119
-
120
- Messages stored in localStorage as JSON. Media attachments are base64 data URLs (not blob URLs -- those don't survive reload). The `conversations.ts` module provides `addMessage()`, `updateLastMessage()`, and `parseMedia()`.
121
-
122
- ### Theming
123
-
124
- Five themes defined via CSS custom properties in `app/globals.css`:
125
- - Dark (default), Glass, Color, Light, System
126
- - Components use semantic tokens: `--bg`, `--text-primary`, `--accent`, `--separator`, etc.
127
- - Theme state managed by `app/providers.tsx` ThemeProvider (localStorage)
128
-
129
- ## Onboarding
130
-
131
- `components/OnboardingWizard.tsx` -- 5-step first-run setup wizard:
132
-
133
- 1. **Welcome** -- portal name, subtitle, operator name (with live sidebar preview)
134
- 2. **Theme** -- pick from available themes (applies live)
135
- 3. **Accent Color** -- color preset grid
136
- 4. **Voice Chat** -- microphone permission test (optional)
137
- 5. **Overview** -- feature summary (Agent Map, Chat, Kanban, Crons, Memory)
138
-
139
- **First-run detection:** checks `localStorage('clawport-onboarded')`. If absent, wizard shows automatically.
140
-
141
- **Mounting:** `OnboardingWizard` is rendered in `app/layout.tsx` (always present, self-hides when not needed).
142
-
143
- **Re-run:** settings page has a button that renders `<OnboardingWizard forceOpen onClose={...} />`. When `forceOpen` is true, the wizard pre-populates from current settings and does not set `clawport-onboarded` on completion.
144
-
145
- ## Environment Safety
146
-
147
- `lib/env.ts` exports `requireEnv(name)` -- throws a clear error with the missing variable name and a pointer to `.env.example`.
148
-
149
- **Critical pattern:** call `requireEnv()` inside functions, never at module top level. This prevents imports from crashing during `next build` or test runs when env vars are not set.
150
-
151
- Used by: `lib/memory.ts`, `lib/cron-runs.ts`, `lib/kanban/chat-store.ts`, `lib/crons.ts`
152
-
153
- ## File Map
154
-
155
- ### API Routes
156
-
157
- | Route | Method | Purpose |
158
- |-------|--------|---------|
159
- | `/api/agents` | GET | All agents from registry + SOUL.md |
160
- | `/api/chat/[id]` | POST | Agent chat -- text (streaming) or vision (send+poll) |
161
- | `/api/crons` | GET | Cron jobs via `openclaw cron list --json` |
162
- | `/api/memory` | GET | Memory files from workspace |
163
- | `/api/tts` | POST | Text-to-speech via OpenClaw |
164
- | `/api/transcribe` | POST | Audio transcription via Whisper |
165
-
166
- ### Core Libraries
167
-
168
- | File | Purpose |
169
- |------|---------|
170
- | `lib/agents.ts` | Agent list builder -- calls `loadRegistry()`, merges SOUL.md |
171
- | `lib/agents-registry.ts` | `loadRegistry()` -- workspace override -> bundled fallback |
172
- | `lib/agents.json` | Bundled default agent registry |
173
- | `lib/anthropic.ts` | Vision pipeline: `hasImageContent`, `extractImageAttachments`, `buildTextPrompt`, `sendViaOpenClaw` (send + poll), `execCli` |
174
- | `lib/audio-recorder.ts` | `createAudioRecorder()` -- MediaRecorder + waveform via AnalyserNode |
175
- | `lib/conversations.ts` | Conversation store with localStorage persistence |
176
- | `lib/crons.ts` | Cron data fetching via CLI |
177
- | `lib/env.ts` | `requireEnv(name)` -- safe env var access with clear errors |
178
- | `lib/multimodal.ts` | `buildApiContent()` -- converts Message+Media to OpenAI API format |
179
- | `lib/settings.ts` | `ClawPortSettings` type, `loadSettings()`, `saveSettings()` (localStorage) |
180
- | `lib/transcribe.ts` | `transcribe(audioBlob)` -- Whisper API with graceful fallback |
181
- | `lib/validation.ts` | `validateChatMessages()` -- validates text + multimodal content arrays |
182
-
183
- ### Chat Components
184
-
185
- | Component | Purpose |
186
- |-----------|---------|
187
- | `ConversationView.tsx` | Main chat: messages, input, recording, paste/drop, file staging. Sends `operatorName` in POST body. |
188
- | `VoiceMessage.tsx` | Waveform playback: play/pause + animated bar visualization |
189
- | `FileAttachment.tsx` | File bubble: icon by type + name + size + download |
190
- | `MediaPreview.tsx` | Pre-send strip of staged attachments with remove buttons |
191
- | `AgentList.tsx` | Desktop agent sidebar with unread badges |
192
-
193
- ### Other Components
194
-
195
- | Component | Purpose |
196
- |-----------|---------|
197
- | `OnboardingWizard.tsx` | 5-step first-run setup wizard (name, theme, accent, mic, overview) |
198
- | `NavLinks.tsx` | Sidebar nav with dynamic operator initials + name from settings |
199
- | `Sidebar.tsx` | Sidebar layout shell |
200
- | `AgentAvatar.tsx` | Agent emoji/image avatar with optional background |
201
- | `DynamicFavicon.tsx` | Updates favicon based on portal emoji/icon settings |
202
-
203
- ### Scripts & CLI
204
-
205
- | File | Purpose |
206
- |------|---------|
207
- | `bin/clawport.mjs` | CLI entry point -- `clawport dev`, `clawport setup`, `clawport status`, etc. Resolves package root via `import.meta.url` |
208
- | `scripts/setup.mjs` | `npm run setup` / `clawport setup` -- auto-detects WORKSPACE_PATH, OPENCLAW_BIN, gateway token; writes `.env.local`. Accepts `--cwd=<path>` flag for CLI usage |
209
-
210
- ## Testing
211
-
212
- 17 test suites, 288 tests total. All in `lib/` directory.
213
-
214
- ```bash
215
- npx vitest run # All tests
216
- npx vitest run lib/anthropic.test.ts # Single suite
217
- npx vitest --watch # Watch mode
218
- ```
219
-
220
- Key test patterns:
221
- - `vi.mock('child_process')` for CLI tests (anthropic.ts)
222
- - `vi.useFakeTimers({ shouldAdvanceTime: true })` for polling tests
223
- - `vi.stubEnv()` for environment variable tests
224
- - jsdom environment for DOM-dependent tests
225
-
226
- ## Conventions
227
-
228
- - No external charting/media libraries -- native Web APIs (Canvas, MediaRecorder, AudioContext)
229
- - Base64 data URLs for all persisted media (not blob URLs)
230
- - CSS custom properties for theming -- no Tailwind color classes directly
231
- - Inline styles referencing CSS vars (e.g., `style={{ color: 'var(--text-primary)' }}`)
232
- - Tests colocated with source: `lib/foo.ts` + `lib/foo.test.ts`
233
- - Agent chat uses `claude-sonnet-4-6` model via OpenClaw gateway
234
- - No em dashes in agent responses (enforced via system prompt)
235
- - Call `requireEnv()` inside functions, not at module top level
236
- - No hardcoded operator names -- use `operatorName` from settings context
237
-
238
- ## Common Tasks
239
-
240
- ### Add a new agent
241
- Edit `lib/agents.json` (or drop a custom `agents.json` into `$WORKSPACE_PATH/clawport/`). Auto-appears in map, chat, and detail pages.
242
-
243
- ### Customize agents for your workspace
244
- Create `$WORKSPACE_PATH/clawport/agents.json` with your own agent entries. ClawPort loads this instead of the bundled default. Format matches `lib/agents.json`.
245
-
246
- ### Re-run onboarding wizard
247
- Go to Settings page and click "Re-run Setup Wizard". This opens the wizard with `forceOpen` so it pre-populates current values and does not reset the `clawport-onboarded` flag.
248
-
249
- ### Add a new setting field
250
- 1. Add the field to `ClawPortSettings` interface in `lib/settings.ts`
251
- 2. Add a default value in `DEFAULTS`
252
- 3. Add parsing logic in `loadSettings()`
253
- 4. Add a setter method in `app/settings-provider.tsx`
254
- 5. Consume via `useSettings()` hook in components
255
-
256
- ### Change the chat model
257
- Edit `app/api/chat/[id]/route.ts` -- change the `model` field in `openai.chat.completions.create()`.
258
-
259
- ### Add a new theme
260
- Add a `[data-theme="name"]` block in `app/globals.css` with all CSS custom properties. Add the theme ID to `lib/themes.ts`.
261
-
262
- ### Debug image pipeline
263
- 1. Check server console for `sendViaOpenClaw execFile error:` or `sendViaOpenClaw: timed out`
264
- 2. Test CLI directly: `openclaw gateway call chat.send --params '{"sessionKey":"agent:main:clawport","idempotencyKey":"test","message":"describe","attachments":[]}' --token <token> --json`
265
- 3. Check history: `openclaw gateway call chat.history --params '{"sessionKey":"agent:main:clawport"}' --token <token> --json`
266
- 4. Verify gateway is running: `openclaw gateway call health --token <token>`
package/SETUP.md DELETED
@@ -1,353 +0,0 @@
1
- # ClawPort -- Setup Guide
2
-
3
- This guide walks you through getting ClawPort running against your own OpenClaw instance. If you just want the quick version, see the [README](README.md).
4
-
5
- ---
6
-
7
- ## Prerequisites
8
-
9
- 1. **Node.js 22+** -- [Download](https://nodejs.org). Verify with `node -v`.
10
- 2. **OpenClaw** -- [Install OpenClaw](https://openclaw.ai) and make sure the CLI works: `openclaw --version`.
11
- 3. **OpenClaw gateway running** -- ClawPort talks to the gateway at `localhost:18789`. Start it before launching the UI.
12
-
13
- ---
14
-
15
- ## 1. Install ClawPort
16
-
17
- ```bash
18
- # Install globally from npm
19
- npm install -g clawport-ui
20
-
21
- # Or clone the repo
22
- git clone https://github.com/JohnRiceML/clawport-ui.git
23
- cd clawport-ui
24
- npm install
25
- ```
26
-
27
- ---
28
-
29
- ## 2. Configure Environment
30
-
31
- The fastest way is the auto-setup script:
32
-
33
- ```bash
34
- # If installed globally via npm
35
- clawport setup
36
-
37
- # Or if running from source
38
- npm run setup
39
- ```
40
-
41
- This auto-detects your `WORKSPACE_PATH`, `OPENCLAW_BIN`, and gateway token from your local OpenClaw installation, shows you what it found, and writes `.env.local` after you confirm.
42
-
43
- If you prefer to configure manually, copy the template and edit:
44
-
45
- ```bash
46
- cp .env.example .env.local
47
- ```
48
-
49
- Open `.env.local` in your editor and set the three required variables.
50
-
51
- ### WORKSPACE_PATH
52
-
53
- The path to your OpenClaw workspace directory. This is where OpenClaw stores agent SOUL files, memory, and other data.
54
-
55
- **Default location:** `~/.openclaw/workspace`
56
-
57
- To verify:
58
-
59
- ```bash
60
- ls ~/.openclaw/workspace
61
- ```
62
-
63
- You should see files like `SOUL.md`, an `agents/` directory, and a `memory/` directory. Use the full absolute path in your `.env.local`:
64
-
65
- ```env
66
- WORKSPACE_PATH=/Users/yourname/.openclaw/workspace
67
- ```
68
-
69
- ### OPENCLAW_BIN
70
-
71
- The absolute path to the `openclaw` CLI binary. ClawPort calls this binary for vision messages, cron listing, and other CLI operations.
72
-
73
- To find it:
74
-
75
- ```bash
76
- which openclaw
77
- ```
78
-
79
- Use whatever that returns:
80
-
81
- ```env
82
- OPENCLAW_BIN=/usr/local/bin/openclaw
83
- ```
84
-
85
- If you installed via nvm or a version manager, the path might be something like `/Users/yourname/.nvm/versions/node/v22.14.0/bin/openclaw`. That's fine -- just use the full path.
86
-
87
- ### OPENCLAW_GATEWAY_TOKEN
88
-
89
- The token that authenticates all API calls to the OpenClaw gateway. Every request ClawPort makes (chat, vision, TTS, transcription) includes this token.
90
-
91
- To find it:
92
-
93
- ```bash
94
- openclaw gateway status
95
- ```
96
-
97
- This should display your gateway configuration including the token. Copy it into your `.env.local`:
98
-
99
- ```env
100
- OPENCLAW_GATEWAY_TOKEN=your-token-here
101
- ```
102
-
103
- ### ELEVENLABS_API_KEY (optional)
104
-
105
- If you want voice indicators on agent profiles, add your ElevenLabs API key. Get one at [elevenlabs.io](https://elevenlabs.io).
106
-
107
- ```env
108
- ELEVENLABS_API_KEY=sk_your-key-here
109
- ```
110
-
111
- If you skip this, everything works normally. Voice indicators just won't appear.
112
-
113
- ---
114
-
115
- ## 3. Start the Gateway
116
-
117
- ClawPort expects the OpenClaw gateway to be running at `localhost:18789`. Start it in a separate terminal:
118
-
119
- ```bash
120
- openclaw gateway run
121
- ```
122
-
123
- Leave this running while you use ClawPort. If the gateway isn't running, chat and all AI features will fail with connection errors.
124
-
125
- ---
126
-
127
- ## 4. Run ClawPort
128
-
129
- ```bash
130
- # If installed globally via npm
131
- clawport dev
132
-
133
- # Or if running from source
134
- npm run dev
135
- ```
136
-
137
- Open [http://localhost:3000](http://localhost:3000).
138
-
139
- ### First-Run Onboarding
140
-
141
- On your first visit, ClawPort launches the **onboarding wizard**. This walks you through:
142
-
143
- - **Naming your portal** -- give your command centre a custom name and subtitle
144
- - **Choosing a theme** -- pick from Dark, Glass, Color, Light, or System
145
- - **Setting an accent color** -- personalize the UI highlight color
146
- - **Customizing your logo** -- upload an icon or choose an emoji
147
- - **Entering your name** -- so the UI knows who the operator is
148
-
149
- All of these can be changed later in the Settings page. The wizard just gets you started quickly.
150
-
151
- ---
152
-
153
- ## 5. Agent Customization
154
-
155
- ### Using the Bundled Registry
156
-
157
- ClawPort ships with a default agent registry at `lib/agents.json`. This is a working example showing a full team hierarchy. It works out of the box if your OpenClaw workspace has matching agent SOUL files.
158
-
159
- ### Using Your Own Agents
160
-
161
- To define your own agent team, create a file at:
162
-
163
- ```
164
- $WORKSPACE_PATH/clawport/agents.json
165
- ```
166
-
167
- For example, if your `WORKSPACE_PATH` is `/Users/yourname/.openclaw/workspace`:
168
-
169
- ```bash
170
- mkdir -p /Users/yourname/.openclaw/workspace/clawport
171
- ```
172
-
173
- Then create `agents.json` in that directory. ClawPort checks for this file on every request. If it exists, it replaces the bundled registry entirely. If it's missing or contains invalid JSON, the bundled default is used as a fallback.
174
-
175
- ### Agent Entry Format
176
-
177
- Your `agents.json` should be an array of agent objects. Here's the minimal required shape:
178
-
179
- ```json
180
- [
181
- {
182
- "id": "my-agent",
183
- "name": "My Agent",
184
- "title": "What this agent does",
185
- "reportsTo": null,
186
- "directReports": [],
187
- "soulPath": "agents/my-agent/SOUL.md",
188
- "voiceId": null,
189
- "color": "#06b6d4",
190
- "emoji": "🤖",
191
- "tools": ["read", "write"],
192
- "memoryPath": null,
193
- "description": "One-liner about this agent."
194
- }
195
- ]
196
- ```
197
-
198
- ### Field Reference
199
-
200
- | Field | Type | Description |
201
- |-------|------|-------------|
202
- | `id` | string | Unique slug for the agent (e.g., `"vera"`) |
203
- | `name` | string | Display name (e.g., `"VERA"`) |
204
- | `title` | string | Role title (e.g., `"Chief Strategy Officer"`) |
205
- | `reportsTo` | string or null | Parent agent `id` for the org chart. `null` for the root. |
206
- | `directReports` | string[] | Array of child agent `id`s |
207
- | `soulPath` | string or null | Path to the agent's SOUL.md, relative to `WORKSPACE_PATH` |
208
- | `voiceId` | string or null | ElevenLabs voice ID (requires `ELEVENLABS_API_KEY`) |
209
- | `color` | string | Hex color for the agent's node in the Org Map |
210
- | `emoji` | string | Emoji shown as the agent's avatar |
211
- | `tools` | string[] | List of tools this agent has access to |
212
- | `memoryPath` | string or null | Path to agent-specific memory (relative to `WORKSPACE_PATH`) |
213
- | `description` | string | One-line description shown in the UI |
214
-
215
- ### Hierarchy Rules
216
-
217
- - Exactly one agent should have `"reportsTo": null` -- this is your root/orchestrator node.
218
- - `directReports` should be consistent with `reportsTo`. If agent B reports to agent A, then A's `directReports` should include B's `id`.
219
- - The Org Map uses these relationships to build the org chart automatically.
220
-
221
- ### Example: Minimal Two-Agent Setup
222
-
223
- ```json
224
- [
225
- {
226
- "id": "boss",
227
- "name": "Boss",
228
- "title": "Orchestrator",
229
- "reportsTo": null,
230
- "directReports": ["worker"],
231
- "soulPath": "SOUL.md",
232
- "voiceId": null,
233
- "color": "#f5c518",
234
- "emoji": "👑",
235
- "tools": ["read", "write", "exec", "message"],
236
- "memoryPath": null,
237
- "description": "Top-level orchestrator."
238
- },
239
- {
240
- "id": "worker",
241
- "name": "Worker",
242
- "title": "Task Runner",
243
- "reportsTo": "boss",
244
- "directReports": [],
245
- "soulPath": "agents/worker/SOUL.md",
246
- "voiceId": null,
247
- "color": "#22c55e",
248
- "emoji": "⚙️",
249
- "tools": ["read", "write"],
250
- "memoryPath": null,
251
- "description": "Handles assigned tasks."
252
- }
253
- ]
254
- ```
255
-
256
- ---
257
-
258
- ## 6. Production Build
259
-
260
- ```bash
261
- # If installed globally via npm
262
- clawport start
263
-
264
- # Or if running from source
265
- npx next build
266
- npm start
267
- ```
268
-
269
- The production server runs on port 3000 by default. The gateway still needs to be running at `localhost:18789`.
270
-
271
- ---
272
-
273
- ## Troubleshooting
274
-
275
- ### "Missing required environment variable: WORKSPACE_PATH"
276
-
277
- Your `.env.local` is missing or the variable isn't set. Make sure you copied `.env.example`:
278
-
279
- ```bash
280
- cp .env.example .env.local
281
- ```
282
-
283
- Then fill in the values. Restart the dev server after changing `.env.local`.
284
-
285
- ### Gateway connection refused / chat not working
286
-
287
- The OpenClaw gateway isn't running. Start it:
288
-
289
- ```bash
290
- openclaw gateway run
291
- ```
292
-
293
- Verify it's reachable:
294
-
295
- ```bash
296
- curl http://localhost:18789/v1/models
297
- ```
298
-
299
- You should get a JSON response. If not, check that nothing else is using port 18789.
300
-
301
- ### No agents showing up
302
-
303
- 1. **Check `WORKSPACE_PATH`** -- make sure it points to a valid OpenClaw workspace directory.
304
- 2. **Check your agents.json** -- if you placed a custom `agents.json` at `$WORKSPACE_PATH/clawport/agents.json`, make sure it's valid JSON. A syntax error will cause a silent fallback to the bundled registry. Test with:
305
- ```bash
306
- cat $WORKSPACE_PATH/clawport/agents.json | python3 -m json.tool
307
- ```
308
- 3. **Check the server console** -- ClawPort logs errors to the terminal where `npm run dev` is running.
309
-
310
- ### Agent SOUL.md not loading
311
-
312
- The `soulPath` in your agents.json is relative to `WORKSPACE_PATH`. If your workspace is at `/Users/you/.openclaw/workspace` and `soulPath` is `"agents/vera/SOUL.md"`, ClawPort will look for `/Users/you/.openclaw/workspace/agents/vera/SOUL.md`.
313
-
314
- Make sure the file exists at that path.
315
-
316
- ### Images not working in chat
317
-
318
- Image messages use the CLI pipeline (`openclaw gateway call chat.send`). Common issues:
319
-
320
- 1. **`OPENCLAW_BIN` path is wrong** -- run `which openclaw` and update `.env.local`.
321
- 2. **Gateway token is wrong** -- verify with `openclaw gateway status`.
322
- 3. **Image too large** -- ClawPort resizes to 1200px max, but extremely large images may still hit limits. Try a smaller image.
323
-
324
- Check the server console for errors like `sendViaOpenClaw execFile error:` or `E2BIG`.
325
-
326
- ### Voice/TTS features not working
327
-
328
- Voice features require `ELEVENLABS_API_KEY` in your `.env.local`. Without it, voice indicators won't appear on agent profiles.
329
-
330
- Audio transcription (speech-to-text) uses Whisper through the OpenClaw gateway and does not require a separate key.
331
-
332
- ### Port 3000 already in use
333
-
334
- Another process is using port 3000. Either stop it or run ClawPort on a different port:
335
-
336
- ```bash
337
- npm run dev -- -p 3001
338
- ```
339
-
340
- ---
341
-
342
- ## Running Tests
343
-
344
- ```bash
345
- npm test # Run all tests via Vitest
346
- npx tsc --noEmit # Type-check (expect 0 errors)
347
- ```
348
-
349
- ---
350
-
351
- ## Developer Guide
352
-
353
- For architecture deep-dives, test patterns, and contribution conventions, see [CLAUDE.md](CLAUDE.md).
package/components.json DELETED
@@ -1,23 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
4
- "rsc": true,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "",
8
- "css": "app/globals.css",
9
- "baseColor": "neutral",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "iconLibrary": "lucide",
14
- "rtl": false,
15
- "aliases": {
16
- "components": "@/components",
17
- "utils": "@/lib/utils",
18
- "ui": "@/components/ui",
19
- "lib": "@/lib",
20
- "hooks": "@/hooks"
21
- },
22
- "registries": {}
23
- }
package/vitest.config.ts DELETED
@@ -1,17 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
- import react from '@vitejs/plugin-react'
3
- import path from 'path'
4
-
5
- export default defineConfig({
6
- plugins: [react()],
7
- resolve: {
8
- alias: {
9
- '@': path.resolve(__dirname, './'),
10
- },
11
- },
12
- test: {
13
- environment: 'jsdom',
14
- include: ['**/*.test.ts', '**/*.test.tsx'],
15
- exclude: ['node_modules', '.next'],
16
- },
17
- })