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.
Files changed (132) hide show
  1. package/.env.example +35 -0
  2. package/BRANDING.md +131 -0
  3. package/CLAUDE.md +252 -0
  4. package/README.md +262 -0
  5. package/SETUP.md +337 -0
  6. package/app/agents/[id]/page.tsx +727 -0
  7. package/app/api/agents/route.ts +12 -0
  8. package/app/api/chat/[id]/route.ts +139 -0
  9. package/app/api/cron-runs/route.ts +13 -0
  10. package/app/api/crons/route.ts +12 -0
  11. package/app/api/kanban/chat/[id]/route.ts +119 -0
  12. package/app/api/kanban/chat-history/[ticketId]/route.ts +36 -0
  13. package/app/api/memory/route.ts +12 -0
  14. package/app/api/transcribe/route.ts +37 -0
  15. package/app/api/tts/route.ts +42 -0
  16. package/app/chat/[id]/page.tsx +10 -0
  17. package/app/chat/page.tsx +200 -0
  18. package/app/crons/page.tsx +870 -0
  19. package/app/docs/page.tsx +399 -0
  20. package/app/favicon.ico +0 -0
  21. package/app/globals.css +692 -0
  22. package/app/kanban/page.tsx +327 -0
  23. package/app/layout.tsx +45 -0
  24. package/app/memory/page.tsx +685 -0
  25. package/app/page.tsx +817 -0
  26. package/app/providers.tsx +37 -0
  27. package/app/settings/page.tsx +901 -0
  28. package/app/settings-provider.tsx +209 -0
  29. package/components/AgentAvatar.tsx +54 -0
  30. package/components/AgentNode.tsx +122 -0
  31. package/components/Breadcrumbs.tsx +126 -0
  32. package/components/DynamicFavicon.tsx +62 -0
  33. package/components/ErrorState.tsx +97 -0
  34. package/components/FeedView.tsx +494 -0
  35. package/components/GlobalSearch.tsx +571 -0
  36. package/components/GridView.tsx +532 -0
  37. package/components/ManorMap.tsx +157 -0
  38. package/components/MobileSidebar.tsx +251 -0
  39. package/components/NavLinks.tsx +271 -0
  40. package/components/OnboardingWizard.tsx +1067 -0
  41. package/components/Sidebar.tsx +115 -0
  42. package/components/ThemeToggle.tsx +108 -0
  43. package/components/chat/AgentList.tsx +537 -0
  44. package/components/chat/ConversationView.tsx +1047 -0
  45. package/components/chat/FileAttachment.tsx +140 -0
  46. package/components/chat/MediaPreview.tsx +111 -0
  47. package/components/chat/VoiceMessage.tsx +139 -0
  48. package/components/crons/PipelineGraph.tsx +327 -0
  49. package/components/crons/WeeklySchedule.tsx +630 -0
  50. package/components/docs/AgentsSection.tsx +209 -0
  51. package/components/docs/ApiReferenceSection.tsx +256 -0
  52. package/components/docs/ArchitectureSection.tsx +221 -0
  53. package/components/docs/ComponentsSection.tsx +253 -0
  54. package/components/docs/CronSystemSection.tsx +235 -0
  55. package/components/docs/DocSection.tsx +346 -0
  56. package/components/docs/GettingStartedSection.tsx +169 -0
  57. package/components/docs/ThemingSection.tsx +257 -0
  58. package/components/docs/TroubleshootingSection.tsx +200 -0
  59. package/components/kanban/AgentPicker.tsx +321 -0
  60. package/components/kanban/CreateTicketModal.tsx +333 -0
  61. package/components/kanban/KanbanBoard.tsx +70 -0
  62. package/components/kanban/KanbanColumn.tsx +166 -0
  63. package/components/kanban/TicketCard.tsx +245 -0
  64. package/components/kanban/TicketDetailPanel.tsx +850 -0
  65. package/components/ui/badge.tsx +48 -0
  66. package/components/ui/button.tsx +64 -0
  67. package/components/ui/card.tsx +92 -0
  68. package/components/ui/dialog.tsx +158 -0
  69. package/components/ui/scroll-area.tsx +58 -0
  70. package/components/ui/separator.tsx +28 -0
  71. package/components/ui/skeleton.tsx +27 -0
  72. package/components/ui/tabs.tsx +91 -0
  73. package/components/ui/tooltip.tsx +57 -0
  74. package/components.json +23 -0
  75. package/docs/API.md +648 -0
  76. package/docs/COMPONENTS.md +1059 -0
  77. package/docs/THEMING.md +795 -0
  78. package/lib/agents-registry.ts +35 -0
  79. package/lib/agents.json +282 -0
  80. package/lib/agents.test.ts +367 -0
  81. package/lib/agents.ts +32 -0
  82. package/lib/anthropic.test.ts +422 -0
  83. package/lib/anthropic.ts +220 -0
  84. package/lib/api-error.ts +16 -0
  85. package/lib/audio-recorder.test.ts +72 -0
  86. package/lib/audio-recorder.ts +169 -0
  87. package/lib/conversations.test.ts +331 -0
  88. package/lib/conversations.ts +117 -0
  89. package/lib/cron-pipelines.test.ts +69 -0
  90. package/lib/cron-pipelines.ts +58 -0
  91. package/lib/cron-runs.test.ts +118 -0
  92. package/lib/cron-runs.ts +67 -0
  93. package/lib/cron-utils.test.ts +222 -0
  94. package/lib/cron-utils.ts +160 -0
  95. package/lib/crons.test.ts +502 -0
  96. package/lib/crons.ts +114 -0
  97. package/lib/env.test.ts +44 -0
  98. package/lib/env.ts +14 -0
  99. package/lib/kanban/automation.test.ts +245 -0
  100. package/lib/kanban/automation.ts +143 -0
  101. package/lib/kanban/chat-store.test.ts +149 -0
  102. package/lib/kanban/chat-store.ts +81 -0
  103. package/lib/kanban/store.test.ts +238 -0
  104. package/lib/kanban/store.ts +98 -0
  105. package/lib/kanban/types.ts +50 -0
  106. package/lib/kanban/useAgentWork.ts +78 -0
  107. package/lib/memory.ts +45 -0
  108. package/lib/multimodal.test.ts +219 -0
  109. package/lib/multimodal.ts +68 -0
  110. package/lib/pipeline.integration.test.ts +343 -0
  111. package/lib/sanitize.ts +194 -0
  112. package/lib/settings.test.ts +137 -0
  113. package/lib/settings.ts +94 -0
  114. package/lib/styles.ts +24 -0
  115. package/lib/themes.ts +9 -0
  116. package/lib/transcribe.test.ts +141 -0
  117. package/lib/transcribe.ts +111 -0
  118. package/lib/types.ts +66 -0
  119. package/lib/utils.ts +6 -0
  120. package/lib/validation.test.ts +132 -0
  121. package/lib/validation.ts +80 -0
  122. package/next.config.ts +7 -0
  123. package/package.json +56 -0
  124. package/postcss.config.mjs +7 -0
  125. package/public/file.svg +1 -0
  126. package/public/globe.svg +1 -0
  127. package/public/next.svg +1 -0
  128. package/public/vercel.svg +1 -0
  129. package/public/window.svg +1 -0
  130. package/scripts/setup.mjs +215 -0
  131. package/tsconfig.json +34 -0
  132. package/vitest.config.ts +17 -0
@@ -0,0 +1,257 @@
1
+ import {
2
+ Heading,
3
+ SubHeading,
4
+ Paragraph,
5
+ CodeBlock,
6
+ InlineCode,
7
+ Table,
8
+ BulletList,
9
+ NumberedList,
10
+ Callout,
11
+ InfoCard,
12
+ } from "./DocSection";
13
+
14
+ export function ThemingSection() {
15
+ return (
16
+ <>
17
+ <Heading>Theming</Heading>
18
+ <Paragraph>
19
+ ClawPort's visual theming is driven entirely by CSS custom properties
20
+ and two React context providers: ThemeProvider and SettingsProvider.
21
+ Five built-in themes are toggled via the sidebar theme picker.
22
+ </Paragraph>
23
+
24
+ <SubHeading>Available Themes</SubHeading>
25
+ <Table
26
+ headers={["ID", "Label", "Description"]}
27
+ rows={[
28
+ [
29
+ <InlineCode key="d">dark</InlineCode>,
30
+ "Dark",
31
+ "Apple Dark Mode. The default theme.",
32
+ ],
33
+ [
34
+ <InlineCode key="g">glass</InlineCode>,
35
+ "Glass",
36
+ "Frosted glass dark variant with translucent surfaces.",
37
+ ],
38
+ [
39
+ <InlineCode key="c">color</InlineCode>,
40
+ "Color",
41
+ "Vibrant purple-indigo variant.",
42
+ ],
43
+ [
44
+ <InlineCode key="l">light</InlineCode>,
45
+ "Light",
46
+ "Apple Light Mode.",
47
+ ],
48
+ [
49
+ <InlineCode key="s">system</InlineCode>,
50
+ "System",
51
+ "Follows the OS prefers-color-scheme setting.",
52
+ ],
53
+ ]}
54
+ />
55
+
56
+ <SubHeading>Three-Layer System</SubHeading>
57
+ <NumberedList
58
+ items={[
59
+ <>
60
+ <strong style={{ color: "var(--text-primary)" }}>
61
+ data-theme attribute on &lt;html&gt;
62
+ </strong>{" "}
63
+ -- Each theme defines a CSS rule block scoped to{" "}
64
+ <InlineCode>[data-theme="id"]</InlineCode>. The dark theme also
65
+ matches <InlineCode>:root</InlineCode>.
66
+ </>,
67
+ <>
68
+ <strong style={{ color: "var(--text-primary)" }}>
69
+ CSS custom properties
70
+ </strong>{" "}
71
+ -- Every color, shadow, radius, and material is expressed as a CSS
72
+ variable. Components consume these via inline styles. No Tailwind
73
+ color classes are used directly.
74
+ </>,
75
+ <>
76
+ <strong style={{ color: "var(--text-primary)" }}>
77
+ ThemeProvider
78
+ </strong>{" "}
79
+ (<InlineCode>app/providers.tsx</InlineCode>) -- React context that
80
+ manages theme state, reads/writes localStorage, and sets the
81
+ data-theme attribute on &lt;html&gt;.
82
+ </>,
83
+ ]}
84
+ />
85
+
86
+ <SubHeading>CSS Custom Properties</SubHeading>
87
+
88
+ <InfoCard title="Backgrounds">
89
+ <Table
90
+ headers={["Token", "Purpose", "Dark Example"]}
91
+ rows={[
92
+ [<InlineCode key="b">--bg</InlineCode>, "Primary page background", "#000000"],
93
+ [
94
+ <InlineCode key="bs">--bg-secondary</InlineCode>,
95
+ "Card / surface background",
96
+ "rgba(28,28,30,1)",
97
+ ],
98
+ [
99
+ <InlineCode key="bt">--bg-tertiary</InlineCode>,
100
+ "Nested surface",
101
+ "rgba(44,44,46,1)",
102
+ ],
103
+ ]}
104
+ />
105
+ </InfoCard>
106
+
107
+ <InfoCard title="Materials (Apple translucent surfaces)">
108
+ <Table
109
+ headers={["Token", "Purpose"]}
110
+ rows={[
111
+ [
112
+ <InlineCode key="mr">--material-regular</InlineCode>,
113
+ "Standard material (sidebar, overlays)",
114
+ ],
115
+ [
116
+ <InlineCode key="mt">--material-thick</InlineCode>,
117
+ "Dense material",
118
+ ],
119
+ [
120
+ <InlineCode key="mn">--material-thin</InlineCode>,
121
+ "Light tint material",
122
+ ],
123
+ [
124
+ <InlineCode key="mu">--material-ultra-thin</InlineCode>,
125
+ "Very subtle tint",
126
+ ],
127
+ ]}
128
+ />
129
+ </InfoCard>
130
+
131
+ <InfoCard title="Text & Fills">
132
+ <Table
133
+ headers={["Token", "Purpose"]}
134
+ rows={[
135
+ [
136
+ <InlineCode key="tp">--text-primary</InlineCode>,
137
+ "Headings, body text",
138
+ ],
139
+ [
140
+ <InlineCode key="ts">--text-secondary</InlineCode>,
141
+ "Labels, supporting text",
142
+ ],
143
+ [
144
+ <InlineCode key="tt">--text-tertiary</InlineCode>,
145
+ "Placeholder, captions",
146
+ ],
147
+ [
148
+ <InlineCode key="fp">--fill-primary</InlineCode>,
149
+ "Primary interactive fill (buttons)",
150
+ ],
151
+ [
152
+ <InlineCode key="fs">--fill-secondary</InlineCode>,
153
+ "Hover fill",
154
+ ],
155
+ [
156
+ <InlineCode key="ft">--fill-tertiary</InlineCode>,
157
+ "Subtle fill (input backgrounds)",
158
+ ],
159
+ ]}
160
+ />
161
+ </InfoCard>
162
+
163
+ <InfoCard title="Accent & System Colors">
164
+ <Table
165
+ headers={["Token", "Purpose"]}
166
+ rows={[
167
+ [
168
+ <InlineCode key="a">--accent</InlineCode>,
169
+ "Primary brand accent (buttons, active states)",
170
+ ],
171
+ [
172
+ <InlineCode key="af">--accent-fill</InlineCode>,
173
+ "Accent at 15% opacity (backgrounds)",
174
+ ],
175
+ [<InlineCode key="sb">--system-blue</InlineCode>, "Links, focus rings"],
176
+ [<InlineCode key="sg">--system-green</InlineCode>, "Success, active toggles"],
177
+ [<InlineCode key="sr">--system-red</InlineCode>, "Errors, destructive actions"],
178
+ [<InlineCode key="so">--system-orange</InlineCode>, "Warnings"],
179
+ [<InlineCode key="sp">--system-purple</InlineCode>, "Tags, highlights"],
180
+ ]}
181
+ />
182
+ </InfoCard>
183
+
184
+ <InfoCard title="Code Blocks">
185
+ <Table
186
+ headers={["Token", "Purpose"]}
187
+ rows={[
188
+ [<InlineCode key="cb">--code-bg</InlineCode>, "Code block background"],
189
+ [<InlineCode key="cbd">--code-border</InlineCode>, "Code block border"],
190
+ [<InlineCode key="ct">--code-text</InlineCode>, "Code text color"],
191
+ ]}
192
+ />
193
+ </InfoCard>
194
+
195
+ <SubHeading>Accent Color Override</SubHeading>
196
+ <Paragraph>
197
+ When the user selects a custom accent color in settings, the
198
+ SettingsProvider applies it as inline styles on{" "}
199
+ <InlineCode>document.documentElement</InlineCode>, overriding the
200
+ theme's <InlineCode>--accent</InlineCode> and{" "}
201
+ <InlineCode>--accent-fill</InlineCode>. Setting it to null reverts to
202
+ the theme default.
203
+ </Paragraph>
204
+
205
+ <SubHeading>Adding a New Theme</SubHeading>
206
+ <NumberedList
207
+ items={[
208
+ <>
209
+ Add the theme ID to the <InlineCode>ThemeId</InlineCode> type
210
+ union and <InlineCode>THEMES</InlineCode> array in{" "}
211
+ <InlineCode>lib/themes.ts</InlineCode>.
212
+ </>,
213
+ <>
214
+ Add a <InlineCode>[data-theme="name"]</InlineCode> block in{" "}
215
+ <InlineCode>app/globals.css</InlineCode> defining every CSS custom
216
+ property token. Copy the dark theme block as a starting point.
217
+ </>,
218
+ "Optionally add theme-specific overrides (body background gradients, component styles).",
219
+ "The ThemeProvider, onboarding wizard, and settings page will automatically pick up the new theme.",
220
+ ]}
221
+ />
222
+
223
+ <Callout type="tip">
224
+ Missing tokens will cause components to render with broken styles.
225
+ Always define every token when creating a new theme -- use the dark theme
226
+ block as a complete template.
227
+ </Callout>
228
+
229
+ <SubHeading>Spacing & Typography</SubHeading>
230
+ <BulletList
231
+ items={[
232
+ <>
233
+ <strong style={{ color: "var(--text-primary)" }}>Spacing</strong>{" "}
234
+ -- 4px grid: <InlineCode>--space-1</InlineCode> (4px) through{" "}
235
+ <InlineCode>--space-16</InlineCode> (64px)
236
+ </>,
237
+ <>
238
+ <strong style={{ color: "var(--text-primary)" }}>Typography</strong>{" "}
239
+ -- Apple HIG scale: <InlineCode>--text-caption2</InlineCode> (11px)
240
+ through <InlineCode>--text-large-title</InlineCode> (34px)
241
+ </>,
242
+ <>
243
+ <strong style={{ color: "var(--text-primary)" }}>Radius</strong>{" "}
244
+ -- <InlineCode>--radius-sm</InlineCode> (6px) through{" "}
245
+ <InlineCode>--radius-2xl</InlineCode> (24px)
246
+ </>,
247
+ <>
248
+ <strong style={{ color: "var(--text-primary)" }}>Easing</strong>{" "}
249
+ -- <InlineCode>--ease-spring</InlineCode> (bouncy),{" "}
250
+ <InlineCode>--ease-smooth</InlineCode> (general),{" "}
251
+ <InlineCode>--ease-snappy</InlineCode> (quick)
252
+ </>,
253
+ ]}
254
+ />
255
+ </>
256
+ );
257
+ }
@@ -0,0 +1,200 @@
1
+ import {
2
+ Heading,
3
+ SubHeading,
4
+ Paragraph,
5
+ CodeBlock,
6
+ InlineCode,
7
+ BulletList,
8
+ NumberedList,
9
+ Callout,
10
+ } from "./DocSection";
11
+
12
+ export function TroubleshootingSection() {
13
+ return (
14
+ <>
15
+ <Heading>Troubleshooting</Heading>
16
+ <Paragraph>
17
+ Common issues and their solutions when running ClawPort.
18
+ </Paragraph>
19
+
20
+ {/* ── Issue 1 ────────────────────────────────────────────── */}
21
+ <SubHeading>
22
+ "Missing required environment variable: WORKSPACE_PATH"
23
+ </SubHeading>
24
+ <Paragraph>
25
+ Your <InlineCode>.env.local</InlineCode> is missing or the variable
26
+ isn't set. Make sure you copied <InlineCode>.env.example</InlineCode>:
27
+ </Paragraph>
28
+ <CodeBlock>{`cp .env.example .env.local`}</CodeBlock>
29
+ <Paragraph>
30
+ Then fill in the values. Restart the dev server after changing{" "}
31
+ <InlineCode>.env.local</InlineCode>.
32
+ </Paragraph>
33
+
34
+ {/* ── Issue 2 ────────────────────────────────────────────── */}
35
+ <SubHeading>Gateway connection refused / chat not working</SubHeading>
36
+ <Paragraph>
37
+ The OpenClaw gateway isn't running. Start it:
38
+ </Paragraph>
39
+ <CodeBlock>{`openclaw gateway run`}</CodeBlock>
40
+ <Paragraph>Verify it's reachable:</Paragraph>
41
+ <CodeBlock>{`curl http://localhost:18789/v1/models`}</CodeBlock>
42
+ <Paragraph>
43
+ You should get a JSON response. If not, check that nothing else is using
44
+ port 18789.
45
+ </Paragraph>
46
+
47
+ {/* ── Issue 3 ────────────────────────────────────────────── */}
48
+ <SubHeading>No agents showing up</SubHeading>
49
+ <NumberedList
50
+ items={[
51
+ <>
52
+ <strong style={{ color: "var(--text-primary)" }}>
53
+ Check WORKSPACE_PATH
54
+ </strong>{" "}
55
+ -- make sure it points to a valid OpenClaw workspace directory.
56
+ </>,
57
+ <>
58
+ <strong style={{ color: "var(--text-primary)" }}>
59
+ Check your agents.json
60
+ </strong>{" "}
61
+ -- if you placed a custom{" "}
62
+ <InlineCode>agents.json</InlineCode> at{" "}
63
+ <InlineCode>$WORKSPACE_PATH/clawport/agents.json</InlineCode>, make
64
+ sure it's valid JSON. A syntax error will cause a silent fallback to
65
+ the bundled registry.
66
+ </>,
67
+ <>
68
+ <strong style={{ color: "var(--text-primary)" }}>
69
+ Check the server console
70
+ </strong>{" "}
71
+ -- ClawPort logs errors to the terminal where{" "}
72
+ <InlineCode>npm run dev</InlineCode> is running.
73
+ </>,
74
+ ]}
75
+ />
76
+ <Callout type="tip">
77
+ Test your agents.json with:{" "}
78
+ <InlineCode>
79
+ cat $WORKSPACE_PATH/clawport/agents.json | python3 -m json.tool
80
+ </InlineCode>
81
+ </Callout>
82
+
83
+ {/* ── Issue 4 ────────────────────────────────────────────── */}
84
+ <SubHeading>Agent SOUL.md not loading</SubHeading>
85
+ <Paragraph>
86
+ The <InlineCode>soulPath</InlineCode> in your agents.json is relative to{" "}
87
+ <InlineCode>WORKSPACE_PATH</InlineCode>. If your workspace is at{" "}
88
+ <InlineCode>/Users/you/.openclaw/workspace</InlineCode> and soulPath is{" "}
89
+ <InlineCode>"agents/vera/SOUL.md"</InlineCode>, ClawPort will look for{" "}
90
+ <InlineCode>
91
+ /Users/you/.openclaw/workspace/agents/vera/SOUL.md
92
+ </InlineCode>
93
+ .
94
+ </Paragraph>
95
+ <Paragraph>Make sure the file exists at that path.</Paragraph>
96
+
97
+ {/* ── Issue 5 ────────────────────────────────────────────── */}
98
+ <SubHeading>Images not working in chat</SubHeading>
99
+ <Paragraph>
100
+ Image messages use the CLI pipeline. Common issues:
101
+ </Paragraph>
102
+ <NumberedList
103
+ items={[
104
+ <>
105
+ <strong style={{ color: "var(--text-primary)" }}>
106
+ OPENCLAW_BIN path is wrong
107
+ </strong>{" "}
108
+ -- run <InlineCode>which openclaw</InlineCode> and update{" "}
109
+ <InlineCode>.env.local</InlineCode>.
110
+ </>,
111
+ <>
112
+ <strong style={{ color: "var(--text-primary)" }}>
113
+ Gateway token is wrong
114
+ </strong>{" "}
115
+ -- verify with{" "}
116
+ <InlineCode>openclaw gateway status</InlineCode>.
117
+ </>,
118
+ <>
119
+ <strong style={{ color: "var(--text-primary)" }}>
120
+ Image too large
121
+ </strong>{" "}
122
+ -- ClawPort resizes to 1200px max, but extremely large images may
123
+ still hit limits. Try a smaller image.
124
+ </>,
125
+ ]}
126
+ />
127
+ <Paragraph>
128
+ Check the server console for errors like{" "}
129
+ <InlineCode>sendViaOpenClaw execFile error:</InlineCode> or{" "}
130
+ <InlineCode>E2BIG</InlineCode>.
131
+ </Paragraph>
132
+
133
+ {/* ── Issue 6 ────────────────────────────────────────────── */}
134
+ <SubHeading>Voice/TTS features not working</SubHeading>
135
+ <Paragraph>
136
+ Voice features require <InlineCode>ELEVENLABS_API_KEY</InlineCode> in
137
+ your <InlineCode>.env.local</InlineCode>. Without it, voice indicators
138
+ won't appear on agent profiles.
139
+ </Paragraph>
140
+ <Paragraph>
141
+ Audio transcription (speech-to-text) uses Whisper through the OpenClaw
142
+ gateway and does not require a separate key.
143
+ </Paragraph>
144
+
145
+ {/* ── Issue 7 ────────────────────────────────────────────── */}
146
+ <SubHeading>Port 3000 already in use</SubHeading>
147
+ <Paragraph>
148
+ Another process is using port 3000. Either stop it or run on a different
149
+ port:
150
+ </Paragraph>
151
+ <CodeBlock>{`npm run dev -- -p 3001`}</CodeBlock>
152
+
153
+ {/* ── Debug Image Pipeline ──────────────────────────────── */}
154
+ <SubHeading>Debug Image Pipeline</SubHeading>
155
+ <Paragraph>
156
+ Step-by-step debugging for the vision (image) chat pipeline:
157
+ </Paragraph>
158
+ <NumberedList
159
+ items={[
160
+ <>
161
+ Check server console for{" "}
162
+ <InlineCode>sendViaOpenClaw execFile error:</InlineCode> or{" "}
163
+ <InlineCode>sendViaOpenClaw: timed out</InlineCode>
164
+ </>,
165
+ <>
166
+ Test CLI directly:
167
+ </>,
168
+ ]}
169
+ />
170
+ <CodeBlock title="terminal">
171
+ {`# Test chat.send
172
+ openclaw gateway call chat.send \\
173
+ --params '{"sessionKey":"agent:main:clawport","idempotencyKey":"test","message":"describe","attachments":[]}' \\
174
+ --token <token> --json
175
+
176
+ # Check history
177
+ openclaw gateway call chat.history \\
178
+ --params '{"sessionKey":"agent:main:clawport"}' \\
179
+ --token <token> --json
180
+
181
+ # Verify gateway health
182
+ openclaw gateway call health --token <token>`}
183
+ </CodeBlock>
184
+
185
+ <SubHeading>Running Tests</SubHeading>
186
+ <CodeBlock title="terminal">
187
+ {`npm test # Run all 288 tests via Vitest
188
+ npx tsc --noEmit # Type-check (expect 0 errors)`}
189
+ </CodeBlock>
190
+
191
+ <Callout type="note">
192
+ All tests are in the <InlineCode>lib/</InlineCode> directory, colocated
193
+ with source files. Key test patterns include{" "}
194
+ <InlineCode>vi.mock('child_process')</InlineCode> for CLI tests,{" "}
195
+ <InlineCode>vi.useFakeTimers</InlineCode> for polling tests, and{" "}
196
+ <InlineCode>vi.stubEnv()</InlineCode> for environment variable tests.
197
+ </Callout>
198
+ </>
199
+ );
200
+ }