Complete development guide for the OWork AI agent platform built with Tauri, FastAPI, and Claude SDK. Covers desktop development, architecture, API conventions, security patterns, and build processes.
You are working with the OWork AI Agent Platform, a desktop application that enables users to create, manage, and chat with customizable AI agents powered by Claude Agent SDK.
This is a **Tauri 2.0 desktop application** with:
The desktop version (`desktop/` directory) is the **primary development target**.
1. **Install dependencies:**
```bash
cd desktop
npm install
```
2. **Configure backend environment:**
```bash
cp backend.env.example ../backend/.env
# Edit ../backend/.env and set ANTHROPIC_API_KEY=sk-ant-xxx
```
3. **Set up Python backend (using uv):**
```bash
cd backend
uv sync
source .venv/bin/activate # On Windows: .venv\Scripts\activate
```
```bash
npm run tauri:dev
npm run test # Watch mode
npm run test:run # Single run
npm run lint
npm run build:all # Full build: backend + frontend + Tauri
npm run build:backend # Only Python backend (PyInstaller)
npm run tauri:build # Only Tauri app (requires backend built first)
```
```bash
cd backend
python main.py
pytest # All tests
pytest tests/test_agent_manager.py -v # Specific file
pytest tests/test_agent_manager.py::test_function_name -v # Single test
```
```
User Input → React Frontend → FastAPI Backend → AgentManager
↓
ClaudeSDKClient
↓
Claude Code CLI
↓
SSE Streaming → UI
```
```
Tauri App
├── React Frontend (Vite bundle)
├── Rust Core (lib.rs)
│ ├── Sidecar lifecycle management
│ ├── Dynamic port assignment (portpicker)
│ └── IPC bridge (Tauri commands)
└── Python Backend (PyInstaller sidecar)
├── FastAPI server (dynamic port)
├── SQLite database
└── ClaudeSDKClient
```
**Key Concepts:**
- macOS: `~/Library/Application Support/Owork/`
- Windows: `%LOCALAPPDATA%\Owork\`
- Linux: `~/.local/share/owork/`
```
backend/
├── main.py # FastAPI entry point
├── config.py # Settings (database_type: sqlite)
├── routers/ # API endpoints (agents, skills, mcp, chat, plugins)
├── core/
│ ├── agent_manager.py # ClaudeSDKClient wrapper, hooks, security
│ ├── session_manager.py # Conversation session storage
│ └── workspace_manager.py # Per-agent isolated workspaces
├── database/
│ ├── sqlite.py # Desktop SQLite implementation
│ └── dynamodb.py # Cloud DynamoDB (alternative)
└── schemas/ # Pydantic models
```
```
desktop/
├── src/
│ ├── services/
│ │ ├── api.ts # Axios client with dynamic port
│ │ ├── tauri.ts # Tauri IPC bridge
│ │ └── *.ts # Service modules with toCamelCase()
│ ├── pages/ # Route components
│ ├── components/ # UI components
│ └── types/ # TypeScript interfaces
├── src-tauri/
│ ├── src/lib.rs # Rust: sidecar management
│ ├── binaries/ # PyInstaller output
│ └── tauri.conf.json # Tauri configuration
└── scripts/
├── build.sh # Full build script
└── build-backend.sh # PyInstaller packaging
```
**Backend uses `snake_case`, Frontend uses `camelCase`.**
Transformation functions in `desktop/src/services/*.ts` handle conversion:
| Service | File | Functions |
|---------|------|-----------|
| Agents | `agents.ts` | `toSnakeCase()`, `toCamelCase()` |
| Skills | `skills.ts` | `toCamelCase()` |
| MCP | `mcp.ts` | `toCamelCase()` |
| Chat | `chat.ts` | `toSessionCamelCase()`, `toMessageCamelCase()` |
1. Add to backend Pydantic model (`backend/schemas/*.py`) - use `snake_case`
2. Add to frontend TypeScript interface (`desktop/src/types/index.ts`) - use `camelCase`
3. **Update the corresponding `toCamelCase()` function** - THIS IS COMMONLY FORGOTTEN!
Example:
```python
class Agent(BaseModel):
new_field: str # snake_case
```
```typescript
// desktop/src/types/index.ts
interface Agent {
newField: string; // camelCase
}
// desktop/src/services/agents.ts
function toCamelCase(agent: any): Agent {
return {
// ... existing fields
newField: agent.new_field, // ADD THIS!
};
}
```
```python
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions, HookMatcher
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant...",
model="claude-sonnet-4-20250514",
permission_mode="bypassPermissions", # or "default", "acceptEdits", "plan"
cwd="/path/to/workspace",
setting_sources=['project', 'user'], # For global user mode
allowed_tools=["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Skill"],
mcp_servers={"server": {"type": "stdio", "command": "...", "args": [...]}},
hooks={"PreToolUse": [HookMatcher(matcher='Bash', hooks=[hook_fn])]}
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Hello!")
async for message in client.receive_response():
# Process streaming SSE messages
```
**Built-in Tools:** `Bash`, `Read`, `Write`, `Edit`, `Glob`, `Grep`, `WebFetch`, `Skill`, `TodoWrite`, `NotebookEdit`
Four-layer defense-in-depth model:
1. **Workspace Isolation**: Per-agent directories in `/tmp/agent-platform-workspaces/{agent_id}/`
2. **Skill Access Control**: PreToolUse hook validates authorized skills
3. **File Tool Access Control**: Permission handler validates file paths
4. **Bash Command Protection**: Regex parsing blocks absolute paths outside workspace
**Agent Modes:**
The app supports **light**, **dark**, and **system** (follows OS preference) themes.
**Key Files:**
**CRITICAL: Never use hardcoded colors!**
```tsx
// ✅ Correct - use CSS variables
className="bg-[var(--color-card)] text-[var(--color-text)] border-[var(--color-border)]"
// ❌ Wrong - hardcoded dark theme colors
className="bg-dark-card text-white border-dark-border"
```
**Available CSS Variables:**
The app supports multiple languages using `i18next`:
**When adding new UI text:**
1. Add the key to both `en.json` and `zh.json`
2. Use `useTranslation()` hook:
```tsx
const { t } = useTranslation();
return <h1>{t('agents.create')}</h1>;
```
3. Use nested keys: `"agents": { "create": "Create Agent" }`
```json
{"type": "assistant", "content": [...], "model": "..."}
{"type": "tool_use", "content": [...]}
{"type": "tool_result", "content": [...]}
{"type": "ask_user_question", "toolUseId": "...", "questions": [...]}
{"type": "permission_request", "requestId": "...", "toolName": "...", "reason": "..."}
{"type": "result", "sessionId": "...", "durationMs": ..., "totalCostUsd": ...}
{"type": "error", "error": "..."}
```
1. Backend schema: `backend/schemas/agent.py`
2. Database: `backend/database/sqlite.py` (add column)
3. Agent manager: `backend/core/agent_manager.py` (use in `_build_options()`)
4. Frontend types: `desktop/src/types/index.ts`
5. Frontend service: `desktop/src/services/agents.ts` (both `toSnakeCase` and `toCamelCase`)
6. Frontend UI: `desktop/src/pages/AgentsPage.tsx`
```bash
tail -f logs/backend.log | grep -E "(PRE-TOOL|BLOCKED|ERROR)"
tail -f logs/backend.log | grep "FILE ACCESS DENIED\|BASH FILE ACCESS\|BLOCKED.*Skill"
```
```env
ANTHROPIC_API_KEY=sk-ant-xxx
DATABASE_TYPE=sqlite
DEFAULT_MODEL=claude-sonnet-4-5-20250929
CLAUDE_CODE_USE_BEDROCK=false
DEBUG=true
HOST=127.0.0.1
PORT=8000
RATE_LIMIT_PER_MINUTE=1000
```
**macOS:**
```
desktop/src-tauri/target/release/bundle/
├── dmg/Owork_*.dmg
└── macos/Owork.app
```
**Windows:**
```
desktop/src-tauri/target/release/bundle/
├── msi/Owork_*_x64.msi
└── nsis/Owork_*_x64-setup.exe
```
**Linux:**
```
desktop/src-tauri/target/release/bundle/
├── deb/owork_*.deb
└── appimage/owork_*.AppImage
```
1. Always use CSS variables for colors (never hardcode theme colors)
2. Always update `toCamelCase()` functions when adding new API fields
3. Never commit `.env` files (use `.env.example` templates)
4. Run `pytest` before committing backend changes
5. Run `npm run lint` before committing frontend changes
6. Desktop version is the primary target (not cloud deployment)
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/owork-desktop-ai-platform-guide/raw