mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 16:30:30 +00:00
feat: add docs chat prototype and related scripts
- Introduced a minimal documentation chatbot that builds a search index from markdown files and serves responses via an API. - Added scripts for building the index and serving the chat API. - Updated package.json with new commands for chat index building and serving. - Created a new Vercel configuration file for deployment. - Added a README for the docs chat prototype detailing usage and integration.
This commit is contained in:
281
docs/assets/docs-chat-widget.js
Normal file
281
docs/assets/docs-chat-widget.js
Normal file
@@ -0,0 +1,281 @@
|
||||
(() => {
|
||||
if (document.getElementById("docs-chat-root")) return;
|
||||
|
||||
const apiBase = window.DOCS_CHAT_API_URL || "http://localhost:3001";
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
#docs-chat-root { position: fixed; right: 20px; bottom: 20px; z-index: 9999; font-family: inherit; }
|
||||
#docs-chat-root.docs-chat-expanded { right: 0; bottom: 0; }
|
||||
:root {
|
||||
--docs-chat-accent: #FF5A36;
|
||||
--docs-chat-text: #121212;
|
||||
--docs-chat-muted: #4b4b4b;
|
||||
--docs-chat-panel: rgba(255, 255, 255, 0.78);
|
||||
--docs-chat-panel-border: rgba(17, 17, 17, 0.08);
|
||||
--docs-chat-surface: rgba(255, 255, 255, 0.6);
|
||||
--docs-chat-shadow: 0 18px 50px rgba(0,0,0,0.18);
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
--docs-chat-text: #ececec;
|
||||
--docs-chat-muted: #b7b7b7;
|
||||
--docs-chat-panel: rgba(20, 20, 20, 0.78);
|
||||
--docs-chat-panel-border: rgba(255, 255, 255, 0.1);
|
||||
--docs-chat-surface: rgba(24, 24, 24, 0.65);
|
||||
--docs-chat-shadow: 0 18px 50px rgba(0,0,0,0.45);
|
||||
}
|
||||
#docs-chat-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: linear-gradient(140deg, rgba(255,90,54,0.25), rgba(255,90,54,0.06));
|
||||
color: var(--docs-chat-text);
|
||||
border: 1px solid rgba(255,90,54,0.4);
|
||||
border-radius: 999px;
|
||||
padding: 10px 14px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 8px 30px rgba(255,90,54, 0.08);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
}
|
||||
#docs-chat-button span { font-weight: 600; letter-spacing: 0.2px; }
|
||||
.docs-chat-logo { width: 20px; height: 20px; }
|
||||
#docs-chat-panel {
|
||||
width: 360px;
|
||||
height: 460px;
|
||||
background: var(--docs-chat-panel);
|
||||
color: var(--docs-chat-text);
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--docs-chat-panel-border);
|
||||
box-shadow: var(--docs-chat-shadow);
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
}
|
||||
#docs-chat-root.docs-chat-expanded #docs-chat-panel {
|
||||
width: min(520px, 100vw);
|
||||
height: 100vh;
|
||||
border-radius: 18px 0 0 18px;
|
||||
}
|
||||
#docs-chat-header {
|
||||
padding: 12px 14px;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid var(--docs-chat-panel-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
#docs-chat-header-title { display: inline-flex; align-items: center; gap: 8px; }
|
||||
#docs-chat-header-title span { color: var(--docs-chat-text); }
|
||||
#docs-chat-header-actions { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.docs-chat-icon-button {
|
||||
border: 1px solid var(--docs-chat-panel-border);
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
border-radius: 8px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
#docs-chat-messages { flex: 1; padding: 12px 14px; overflow: auto; background: transparent; }
|
||||
#docs-chat-input {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 14px;
|
||||
border-top: 1px solid var(--docs-chat-panel-border);
|
||||
background: var(--docs-chat-surface);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
#docs-chat-input textarea {
|
||||
flex: 1;
|
||||
resize: none;
|
||||
border: 1px solid var(--docs-chat-panel-border);
|
||||
border-radius: 10px;
|
||||
padding: 9px 10px;
|
||||
font-size: 13px;
|
||||
color: var(--docs-chat-text);
|
||||
background: rgba(255,255,255,0.7);
|
||||
}
|
||||
html[data-theme="dark"] #docs-chat-input textarea { background: rgba(15,15,15,0.7); }
|
||||
#docs-chat-send {
|
||||
background: var(--docs-chat-accent);
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
.docs-chat-bubble {
|
||||
margin-bottom: 10px;
|
||||
padding: 9px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
max-width: 90%;
|
||||
}
|
||||
.docs-chat-user {
|
||||
background: var(--docs-chat-accent);
|
||||
color: #fff;
|
||||
align-self: flex-end;
|
||||
}
|
||||
.docs-chat-assistant {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
color: var(--docs-chat-text);
|
||||
border: 1px solid var(--docs-chat-panel-border);
|
||||
}
|
||||
html[data-theme="dark"] .docs-chat-assistant { background: rgba(20, 20, 20, 0.7); }
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.id = "docs-chat-root";
|
||||
|
||||
const button = document.createElement("button");
|
||||
button.id = "docs-chat-button";
|
||||
button.type = "button";
|
||||
button.innerHTML =
|
||||
`<img class="docs-chat-logo" src="/assets/pixel-lobster.svg" alt="OpenClaw">` +
|
||||
`<span>Ask Molty</span>`;
|
||||
|
||||
const panel = document.createElement("div");
|
||||
panel.id = "docs-chat-panel";
|
||||
panel.style.display = "none";
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.id = "docs-chat-header";
|
||||
header.innerHTML =
|
||||
`<div id="docs-chat-header-title">` +
|
||||
`<img class="docs-chat-logo" src="/assets/pixel-lobster.svg" alt="OpenClaw">` +
|
||||
`<span>OpenClaw Docs</span>` +
|
||||
`</div>` +
|
||||
`<div id="docs-chat-header-actions"></div>`;
|
||||
const headerActions = header.querySelector("#docs-chat-header-actions");
|
||||
const expand = document.createElement("button");
|
||||
expand.type = "button";
|
||||
expand.className = "docs-chat-icon-button";
|
||||
expand.setAttribute("aria-label", "Expand");
|
||||
expand.textContent = "⤢";
|
||||
const clear = document.createElement("button");
|
||||
clear.type = "button";
|
||||
clear.className = "docs-chat-icon-button";
|
||||
clear.setAttribute("aria-label", "Clear chat");
|
||||
clear.textContent = "⌫";
|
||||
const close = document.createElement("button");
|
||||
close.type = "button";
|
||||
close.className = "docs-chat-icon-button";
|
||||
close.setAttribute("aria-label", "Close");
|
||||
close.textContent = "×";
|
||||
headerActions.appendChild(expand);
|
||||
headerActions.appendChild(clear);
|
||||
headerActions.appendChild(close);
|
||||
|
||||
const messages = document.createElement("div");
|
||||
messages.id = "docs-chat-messages";
|
||||
|
||||
const inputWrap = document.createElement("div");
|
||||
inputWrap.id = "docs-chat-input";
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.rows = 2;
|
||||
textarea.placeholder = "Ask about OpenClaw Docs...";
|
||||
const send = document.createElement("button");
|
||||
send.id = "docs-chat-send";
|
||||
send.type = "button";
|
||||
send.textContent = "Send";
|
||||
|
||||
inputWrap.appendChild(textarea);
|
||||
inputWrap.appendChild(send);
|
||||
|
||||
panel.appendChild(header);
|
||||
panel.appendChild(messages);
|
||||
panel.appendChild(inputWrap);
|
||||
|
||||
root.appendChild(button);
|
||||
root.appendChild(panel);
|
||||
document.body.appendChild(root);
|
||||
|
||||
const addBubble = (text, role) => {
|
||||
const bubble = document.createElement("div");
|
||||
bubble.className =
|
||||
"docs-chat-bubble " +
|
||||
(role === "user" ? "docs-chat-user" : "docs-chat-assistant");
|
||||
bubble.textContent = text;
|
||||
messages.appendChild(bubble);
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
return bubble;
|
||||
};
|
||||
|
||||
let isExpanded = false;
|
||||
const setOpen = (isOpen) => {
|
||||
panel.style.display = isOpen ? "flex" : "none";
|
||||
button.style.display = isOpen ? "none" : "inline-flex";
|
||||
root.classList.toggle("docs-chat-expanded", isOpen && isExpanded);
|
||||
if (isOpen) textarea.focus();
|
||||
};
|
||||
|
||||
const setExpanded = (next) => {
|
||||
isExpanded = next;
|
||||
expand.textContent = isExpanded ? "⤡" : "⤢";
|
||||
expand.setAttribute("aria-label", isExpanded ? "Collapse" : "Expand");
|
||||
if (panel.style.display !== "none") {
|
||||
root.classList.toggle("docs-chat-expanded", isExpanded);
|
||||
}
|
||||
};
|
||||
|
||||
button.addEventListener("click", () => setOpen(true));
|
||||
expand.addEventListener("click", () => setExpanded(!isExpanded));
|
||||
clear.addEventListener("click", () => {
|
||||
messages.innerHTML = "";
|
||||
});
|
||||
close.addEventListener("click", () => {
|
||||
setOpen(false);
|
||||
root.classList.remove("docs-chat-expanded");
|
||||
});
|
||||
|
||||
const sendMessage = async () => {
|
||||
const text = textarea.value.trim();
|
||||
if (!text) return;
|
||||
textarea.value = "";
|
||||
addBubble(text, "user");
|
||||
const assistantBubble = addBubble("...", "assistant");
|
||||
assistantBubble.textContent = "";
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiBase}/chat`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ message: text }),
|
||||
});
|
||||
if (!response.body) {
|
||||
assistantBubble.textContent = await response.text();
|
||||
return;
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let fullText = "";
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
fullText += decoder.decode(value, { stream: true });
|
||||
assistantBubble.textContent = fullText;
|
||||
messages.scrollTop = messages.scrollHeight;
|
||||
}
|
||||
} catch (err) {
|
||||
assistantBubble.textContent = "Failed to reach docs chat API.";
|
||||
}
|
||||
};
|
||||
|
||||
send.addEventListener("click", sendMessage);
|
||||
textarea.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
})();
|
||||
208
docs/start/index.md
Normal file
208
docs/start/index.md
Normal file
@@ -0,0 +1,208 @@
|
||||
---
|
||||
summary: "Beginner guide: from zero to first message (wizard, auth, channels, pairing)"
|
||||
read_when:
|
||||
- First time setup from zero
|
||||
- You want the fastest path from install → onboarding → first message
|
||||
title: "Index"
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
|
||||
Goal: go from **zero** → **first working chat** (with sane defaults) as quickly as possible.
|
||||
|
||||
Fastest chat: open the Control UI (no channel setup needed). Run `openclaw dashboard`
|
||||
and chat in the browser, or open `http://127.0.0.1:18789/` on the gateway host.
|
||||
Docs: [Dashboard](/web/dashboard) and [Control UI](/web/control-ui).
|
||||
|
||||
Recommended path: use the **CLI onboarding wizard** (`openclaw onboard`). It sets up:
|
||||
|
||||
- model/auth (OAuth recommended)
|
||||
- gateway settings
|
||||
- channels (WhatsApp/Telegram/Discord/Mattermost (plugin)/...)
|
||||
- pairing defaults (secure DMs)
|
||||
- workspace bootstrap + skills
|
||||
- optional background service
|
||||
|
||||
If you want the deeper reference pages, jump to: [Wizard](/start/wizard), [Setup](/start/setup), [Pairing](/start/pairing), [Security](/gateway/security).
|
||||
|
||||
Sandboxing note: `agents.defaults.sandbox.mode: "non-main"` uses `session.mainKey` (default `"main"`),
|
||||
so group/channel sessions are sandboxed. If you want the main agent to always
|
||||
run on host, set an explicit per-agent override:
|
||||
|
||||
```json
|
||||
{
|
||||
"routing": {
|
||||
"agents": {
|
||||
"main": {
|
||||
"workspace": "~/.openclaw/workspace",
|
||||
"sandbox": { "mode": "off" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 0) Prereqs
|
||||
|
||||
- Node `>=22`
|
||||
- `pnpm` (optional; recommended if you build from source)
|
||||
- **Recommended:** Brave Search API key for web search. Easiest path:
|
||||
`openclaw configure --section web` (stores `tools.web.search.apiKey`).
|
||||
See [Web tools](/tools/web).
|
||||
|
||||
macOS: if you plan to build the apps, install Xcode / CLT. For the CLI + gateway only, Node is enough.
|
||||
Windows: use **WSL2** (Ubuntu recommended). WSL2 is strongly recommended; native Windows is untested, more problematic, and has poorer tool compatibility. Install WSL2 first, then run the Linux steps inside WSL. See [Windows (WSL2)](/platforms/windows).
|
||||
|
||||
## 1) Install the CLI (recommended)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://openclaw.ai/install.sh | bash
|
||||
```
|
||||
|
||||
Installer options (install method, non-interactive, from GitHub): [Install](/install).
|
||||
|
||||
Windows (PowerShell):
|
||||
|
||||
```powershell
|
||||
iwr -useb https://openclaw.ai/install.ps1 | iex
|
||||
```
|
||||
|
||||
Alternative (global install):
|
||||
|
||||
```bash
|
||||
npm install -g openclaw@latest
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm add -g openclaw@latest
|
||||
```
|
||||
|
||||
## 2) Run the onboarding wizard (and install the service)
|
||||
|
||||
```bash
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
What you’ll choose:
|
||||
|
||||
- **Local vs Remote** gateway
|
||||
- **Auth**: OpenAI Code (Codex) subscription (OAuth) or API keys. For Anthropic we recommend an API key; `claude setup-token` is also supported.
|
||||
- **Providers**: WhatsApp QR login, Telegram/Discord bot tokens, Mattermost plugin tokens, etc.
|
||||
- **Daemon**: background install (launchd/systemd; WSL2 uses systemd)
|
||||
- **Runtime**: Node (recommended; required for WhatsApp/Telegram). Bun is **not recommended**.
|
||||
- **Gateway token**: the wizard generates one by default (even on loopback) and stores it in `gateway.auth.token`.
|
||||
|
||||
Wizard doc: [Wizard](/start/wizard)
|
||||
|
||||
### Auth: where it lives (important)
|
||||
|
||||
- **Recommended Anthropic path:** set an API key (wizard can store it for service use). `claude setup-token` is also supported if you want to reuse Claude Code credentials.
|
||||
|
||||
- OAuth credentials (legacy import): `~/.openclaw/credentials/oauth.json`
|
||||
- Auth profiles (OAuth + API keys): `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`
|
||||
|
||||
Headless/server tip: do OAuth on a normal machine first, then copy `oauth.json` to the gateway host.
|
||||
|
||||
## 3) Start the Gateway
|
||||
|
||||
If you installed the service during onboarding, the Gateway should already be running:
|
||||
|
||||
```bash
|
||||
openclaw gateway status
|
||||
```
|
||||
|
||||
Manual run (foreground):
|
||||
|
||||
```bash
|
||||
openclaw gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
Dashboard (local loopback): `http://127.0.0.1:18789/`
|
||||
If a token is configured, paste it into the Control UI settings (stored as `connect.params.auth.token`).
|
||||
|
||||
⚠️ **Bun warning (WhatsApp + Telegram):** Bun has known issues with these
|
||||
channels. If you use WhatsApp or Telegram, run the Gateway with **Node**.
|
||||
|
||||
## 3.5) Quick verify (2 min)
|
||||
|
||||
```bash
|
||||
openclaw status
|
||||
openclaw health
|
||||
openclaw security audit --deep
|
||||
```
|
||||
|
||||
## 4) Pair + connect your first chat surface
|
||||
|
||||
### WhatsApp (QR login)
|
||||
|
||||
```bash
|
||||
openclaw channels login
|
||||
```
|
||||
|
||||
Scan via WhatsApp → Settings → Linked Devices.
|
||||
|
||||
WhatsApp doc: [WhatsApp](/channels/whatsapp)
|
||||
|
||||
### Telegram / Discord / others
|
||||
|
||||
The wizard can write tokens/config for you. If you prefer manual config, start with:
|
||||
|
||||
- Telegram: [Telegram](/channels/telegram)
|
||||
- Discord: [Discord](/channels/discord)
|
||||
- Mattermost (plugin): [Mattermost](/channels/mattermost)
|
||||
|
||||
**Telegram DM tip:** your first DM returns a pairing code. Approve it (see next step) or the bot won’t respond.
|
||||
|
||||
## 5) DM safety (pairing approvals)
|
||||
|
||||
Default posture: unknown DMs get a short code and messages are not processed until approved.
|
||||
If your first DM gets no reply, approve the pairing:
|
||||
|
||||
```bash
|
||||
openclaw pairing list whatsapp
|
||||
openclaw pairing approve whatsapp <code>
|
||||
```
|
||||
|
||||
Pairing doc: [Pairing](/start/pairing)
|
||||
|
||||
## From source (development)
|
||||
|
||||
If you’re hacking on OpenClaw itself, run from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openclaw/openclaw.git
|
||||
cd openclaw
|
||||
pnpm install
|
||||
pnpm ui:build # auto-installs UI deps on first run
|
||||
pnpm build
|
||||
openclaw onboard --install-daemon
|
||||
```
|
||||
|
||||
If you don’t have a global install yet, run the onboarding step via `pnpm openclaw ...` from the repo.
|
||||
`pnpm build` also bundles A2UI assets; if you need to run just that step, use `pnpm canvas:a2ui:bundle`.
|
||||
|
||||
Gateway (from this repo):
|
||||
|
||||
```bash
|
||||
node openclaw.mjs gateway --port 18789 --verbose
|
||||
```
|
||||
|
||||
## 7) Verify end-to-end
|
||||
|
||||
In a new terminal, send a test message:
|
||||
|
||||
```bash
|
||||
openclaw message send --target +15555550123 --message "Hello from OpenClaw"
|
||||
```
|
||||
|
||||
If `openclaw health` shows “no auth configured”, go back to the wizard and set OAuth/key auth — the agent won’t be able to respond without it.
|
||||
|
||||
Tip: `openclaw status --all` is the best pasteable, read-only debug report.
|
||||
Health probes: `openclaw health` (or `openclaw status --deep`) asks the running gateway for a health snapshot.
|
||||
|
||||
## Next steps (optional, but great)
|
||||
|
||||
- macOS menu bar app + voice wake: [macOS app](/platforms/macos)
|
||||
- iOS/Android nodes (Canvas/camera/voice): [Nodes](/nodes)
|
||||
- Remote access (SSH tunnel / Tailscale Serve): [Remote access](/gateway/remote) and [Tailscale](/gateway/tailscale)
|
||||
- Always-on / VPN setups: [Remote access](/gateway/remote), [exe.dev](/platforms/exe-dev), [Hetzner](/platforms/hetzner), [macOS remote](/platforms/mac/remote)
|
||||
5
docs/vercel.json
Normal file
5
docs/vercel.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"installCommand": "bundle install",
|
||||
"buildCommand": "bundle exec jekyll build",
|
||||
"outputDirectory": "_site"
|
||||
}
|
||||
Reference in New Issue
Block a user