Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
8 changes: 7 additions & 1 deletion .cursor/rules.md → .cursor/rules/mintlify.mdc
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
---
description: Mintlify technical writing guidelines — components, style, and documentation standards for the docs site.
globs: docs/**/*.mdx, docs/**/*.md
alwaysApply: false
---

# Mintlify technical writing rule

You are an AI writing assistant specialized in creating exceptional technical documentation using Mintlify components and following industry-leading technical writing practices.
Expand Down Expand Up @@ -392,4 +398,4 @@ description: "Concise description explaining page purpose and value"
- Use **Accordions** for progressive disclosure of information
- Use **RequestExample/ResponseExample** specifically for API endpoint documentation
- Use **ParamField** for API parameters, **ResponseField** for API responses
- Use **Expandable** for nested object properties or hierarchical information
- Use **Expandable** for nested object properties or hierarchical information
124 changes: 124 additions & 0 deletions .cursor/rules/openclaw.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
description: Context for the OpenClaw project at ~/openclaw — commands, structure, tech stack, and conventions.
globs: plugin/**
alwaysApply: false
---

# OpenClaw Project Context

OpenClaw lives at `~/openclaw`. It is a separate repository from this workspace.
All commands below must be run from `~/openclaw` (use `working_directory` or `cd ~/openclaw`).

## What It Is

Multi-channel personal AI assistant gateway (Node.js/TypeScript). Connects messaging platforms (WhatsApp, Telegram, Slack, Discord, Signal, iMessage, etc.) to an AI agent.

## Package Manager

**pnpm** (v10.23.0, declared via `packageManager` in `package.json`).
Always use `pnpm`, never `npm` or `yarn`.

## Repository Structure

```
openclaw/
├── src/ # Core source (gateway, agents, CLI, channels, config, infra)
├── extensions/ # Channel/feature plugins (whatsapp, telegram, discord, slack, etc.)
├── ui/ # Control UI (Lit + Vite web dashboard)
├── packages/ # Sub-packages (clawdbot, moltbot)
├── apps/ # Native apps (macos, ios, android)
├── scripts/ # Build/dev/test helper scripts
├── skills/ # Bundled skills
├── docs/ # Documentation (Mintlify)
├── dist/ # Built output
├── openclaw.mjs # CLI entry point
├── tsdown.config.ts # Bundler config
├── tsconfig.json # TypeScript config (experimentalDecorators: true)
└── vitest.*.config.ts # Test configs (unit, e2e, live, gateway, extensions)
```

## Tech Stack

| Layer | Technology |
|----------------|-----------------------------------|
| Language | TypeScript (strict) |
| Runtime | Node.js >= 22 |
| Bundler | tsdown |
| Formatter | oxfmt |
| Linter | oxlint (type-aware) |
| Type checker | TypeScript / tsgo (native preview)|
| Test runner | Vitest |
| UI framework | Lit (Control UI) |
| UI bundler | Vite |
| Package manager| pnpm 10.23.0 |

## Commands (run from ~/openclaw)

### Setup
```bash
pnpm install
pnpm ui:install # Install UI deps (auto-runs on ui:build)
```

### Build
```bash
pnpm build # Full production build
pnpm ui:build # Build the Control UI
```

### Development
```bash
pnpm dev # Run node via tsx (auto-reload)
pnpm gateway:dev # Gateway in dev mode (skips channels)
pnpm gateway:watch # Watch mode with auto-reload on TS changes
pnpm ui:dev # Vite dev server for Control UI
pnpm tui # Terminal UI
pnpm tui:dev # TUI in dev profile
```

### Running CLI from Source
```bash
pnpm openclaw # Run CLI via tsx
pnpm openclaw onboard --install-daemon # Setup wizard
pnpm openclaw gateway --port 18789 --verbose
pnpm openclaw agent --message "Hello"
pnpm openclaw doctor # Diagnose config issues
```

### Testing
```bash
pnpm test # All tests (parallel)
pnpm test:fast # Unit tests only (fastest)
pnpm test:e2e # End-to-end tests
pnpm test:live # Live tests (needs OPENCLAW_LIVE_TEST=1)
pnpm test:watch # Vitest watch mode
pnpm test:coverage # Unit tests with coverage
pnpm test:ui # Control UI tests
```

### Linting / Formatting / Type-checking
```bash
pnpm check # format:check + tsgo + lint (full CI check)
pnpm lint # oxlint with type-aware rules
pnpm lint:fix # Auto-fix lint + reformat
pnpm format # Format code (oxfmt --write)
pnpm format:check # Check formatting only
```

### Pre-PR Checklist
```bash
pnpm build && pnpm check && pnpm test
```

### Docs
```bash
pnpm docs:dev # Local Mintlify docs dev server
pnpm check:docs # Check docs formatting + links
```

## Key Conventions

- UI uses Lit with **legacy decorators** (`@state()`, `@property()`), not standard `accessor` decorators.
- `tsconfig.json` has `experimentalDecorators: true` and `useDefineForClassFields: false`.
- Entry point is `openclaw.mjs` which delegates to `dist/index.js` (or tsx in dev).
- Extensions are self-contained plugins under `extensions/` with their own `package.json`.
40 changes: 40 additions & 0 deletions docs/ai-rules.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: "AI rules"
description: "Cursor rule files that give AI agents project context automatically"
---

LogLife includes `.cursor/rules/` files that feed project-specific context to [Cursor](https://cursor.com) (or any AI editor that supports the format). When you open the repository in Cursor, the agent already knows the tech stack, available commands, and coding conventions — no pasting of README snippets needed.

## How it works

Cursor reads `.mdc` files from `.cursor/rules/` and injects their contents into every AI conversation. Each file has YAML frontmatter that controls when it activates:

- **`alwaysApply: true`** — injected into every conversation automatically.
- **`globs: "plugin/**"`** — only injected when you have matching files open.
- **`alwaysApply: false`** (no globs) — available in the rule picker but never auto-injected.

## Current rules

| File | Activates | What it provides |
|------|-----------|-----------------|
| `loglife.mdc` | Always | Repository structure, package manager (`pnpm`), tech stack (Next.js, Clerk, Tailwind, Vapi), key conventions, and available commands |
| `openclaw.mdc` | When editing `plugin/` files | OpenClaw project context at `~/openclaw` — build/dev/test commands, repo structure, and conventions (tsdown, oxlint, Lit decorators) |
| `mintlify.mdc` | When editing `docs/` files | Mintlify component reference, writing style guide, and documentation standards |

## Adding or editing rules

Rules live in `.cursor/rules/` at the repository root. To add a new rule, create a `.mdc` file with the appropriate frontmatter:

```yaml
---
description: Short description shown in the rule picker
globs: src/**/*.ts
alwaysApply: false
---
```

Then write the rule body in Markdown below the frontmatter. Keep rules concise and actionable — they are injected as context into the AI's prompt, so unnecessary length wastes tokens.

<Tip>
If you are not using Cursor, these files have no effect on your workflow. They are ignored by the build, linter, and deployment pipeline.
</Tip>
4 changes: 4 additions & 0 deletions docs/api-reference/endpoint/unregister.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "Unregister User"
openapi: "POST /loglife/unregister"
---
4 changes: 4 additions & 0 deletions docs/api-reference/endpoint/users.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: "List Users"
openapi: "GET /loglife/users"
---
21 changes: 12 additions & 9 deletions docs/api-reference/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ description: "The LogLife plugin exposes an HTTP API inside the OpenClaw gateway

## Overview

The LogLife plugin registers four HTTP routes on the OpenClaw gateway:
The LogLife plugin registers six HTTP routes on the OpenClaw gateway:

| Endpoint | Method | Purpose |
|---|---|---|
| `/loglife/sessions` | GET | Look up session data by phone, session ID, or key |
| `/loglife/verify/send` | POST | Send a 6-digit verification code via WhatsApp |
| `/loglife/verify/check` | POST | Validate a verification code |
| `/loglife/register` | POST | Register a new user in the multi-user configuration |
| `/loglife/unregister` | POST | Remove a user from the multi-user configuration |
| `/loglife/users` | GET | List currently registered users (monitoring/testing) |

## Authentication

Expand All @@ -38,17 +40,17 @@ Browser → Next.js API route → LogLife Plugin (OpenClaw gateway)

## User registration flow

When a new user signs up on the dashboard, the verification and registration endpoints work together:
In V1, registration happens before code verification:

1. Dashboard calls `/loglife/verify/send` with the user's phone number
2. Plugin sends a 6-digit code via WhatsApp
3. User enters the code on the dashboard
4. Dashboard calls `/loglife/verify/check` to validate the code
5. On success, dashboard calls `/loglife/register` with the phone and user's name
6. Plugin adds the user to the multi-user config and triggers a gateway hot-reload
1. Dashboard calls `/loglife/register` with the user's phone number
2. Plugin adds the user to the multi-user config and updates gateway config
3. Dashboard calls `/loglife/verify/send` with the same phone number
4. Plugin sends a 6-digit code via WhatsApp
5. User enters the code on the dashboard
6. Dashboard calls `/loglife/verify/check` to validate the code
7. The user can now send messages to the bot via WhatsApp

No gateway restart is required — the hot-reload picks up the new configuration immediately.
No gateway restart is required.

## Security model

Expand All @@ -58,5 +60,6 @@ No gateway restart is required — the hot-reload picks up the new configuration
- **Single-use codes** deleted immediately after successful verification
- **5-minute TTL** on verification codes
- **Idempotent registration** — registering an already-registered phone returns success without duplicating
- **Idempotent unregistration** — unregistering an unknown phone returns `removed: false`

Documenting these endpoints publicly is safe because knowing the URL structure and parameters is useless without the API key, which is only stored server-side.
85 changes: 85 additions & 0 deletions docs/api-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,91 @@
}
}
}
},
"/loglife/unregister": {
"post": {
"operationId": "unregisterUser",
"summary": "Remove a registered user",
"description": "Removes a user from `users.json` by phone number, regenerates config, and updates gateway config. Idempotent — returns removed:false if the phone is not registered.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["phone"],
"properties": {
"phone": {
"type": "string",
"description": "Phone number in E.164 format",
"example": "+15551234567"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Unregister result",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"removed": { "type": "boolean" },
"existing": { "type": "boolean", "description": "False when no matching user exists" },
"removedUserIds": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}
},
"400": { "description": "Missing or invalid phone number" },
"401": { "description": "Unauthorized" },
"500": { "description": "Unregister failed" }
}
}
},
"/loglife/users": {
"get": {
"operationId": "listUsers",
"summary": "List registered users",
"description": "Returns the current users list from `users.json`. Useful for development and monitoring.",
"responses": {
"200": {
"description": "Current users list",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"count": { "type": "integer", "example": 2 },
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"identifiers": {
"type": "array",
"items": { "type": "string" }
}
}
}
}
}
}
}
}
},
"401": { "description": "Unauthorized" }
}
}
}
}
}
22 changes: 18 additions & 4 deletions docs/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ flowchart LR

### Generated configuration

The multi-user system produces a `generated.json` file that is included in `openclaw.json` via the `$include` directive. This file contains:
The multi-user system produces a `generated.json` file that contains:

- **`agents.list`** — one agent definition per user (ID, name, model, skills)
- **`bindings`** — routing rules mapping `{channel, peerId}` to an agent
- **`channels`** — per-channel allow-lists and DM policies
- **`session`** — session scoping settings
- **`env`** — shared environment variables (API keys used by all users)

At runtime, the plugin merges this generated structure directly into `openclaw.json` after register/unregister operations. This avoids relying on `$include` during hot reload.

### Session scoping

The default session scope is `main`, which gives each agent one continuous conversation regardless of which channel the user messages from. This is the correct setting for journaling — your journal is your journal, whether you write from WhatsApp or Telegram.
Expand All @@ -66,10 +68,22 @@ New users are added at runtime through the [`/loglife/register`](/api-reference/
1. The plugin reads `users.json` (the source of truth for all users)
2. Appends the new user with their phone number as an identifier
3. Calls `generateConfig()` to rebuild `generated.json`
4. Writes the updated config
5. Touches `openclaw.json` to trigger a gateway hot-reload
4. Merges generated `agents`, `bindings`, `channels`, and `session` into `openclaw.json`
5. Writes `openclaw.json` (no restart required)

The gateway picks up the new agent, binding, and allow-list entry immediately.

### Runtime user removal

Users can be removed at runtime through [`/loglife/unregister`](/api-reference/endpoint/unregister):

1. The plugin reads `users.json`
2. Removes the matching user by phone identifier
3. Rebuilds `generated.json`
4. Merges the updated generated config into `openclaw.json`
5. Cleans stale allow-list fields when a channel no longer has managed users

The gateway picks up the new agent, binding, and allow-list entry immediately — **no restart required**.
This keeps add/remove symmetric and prevents stale access rules.

<Info>
The full rebuild approach regenerates `generated.json` from scratch on every registration. This is fast enough for the foreseeable scale — under 50ms at 1,000 users, about 1-2 seconds at 100,000 users.
Expand Down
3 changes: 2 additions & 1 deletion docs/contributing-docs.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: "Contributing to docs"
sidebarTitle: "Documentation"
description: "How to update, preview, and deploy the LogLife documentation site."
---

Expand All @@ -9,7 +10,7 @@ The docs live in the `docs/` directory of the LogLife monorepo and are built wit

## How to update

All pages are `.mdx` files in `docs/`. The full Mintlify syntax reference is stored in `.cursor/rules.md` at the repo root, so if you're using an AI editor you can just ask it to make changes and it will follow the correct syntax.
All pages are `.mdx` files in `docs/`. The full Mintlify syntax reference is stored in `.cursor/rules/mintlify.mdc`, so if you're using Cursor (or another AI editor that supports rule files) you can just ask it to make changes and it will follow the correct syntax. See [AI rules](/ai-rules) for details.

To add or edit a page manually:

Expand Down
Loading