mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 17:45:42 +00:00
dd0889e3d6
Introduce a SessionManager for relay-based remote sessions in the Tauri MGMT client: new SessionCommand API, start/stop/session input routing, clipboard/recording/quality controls, and notification read/dismiss state. Wire AppState with new mutexes and show main window on startup. CI: add SBOM generation (anchore) and Trivy vulnerability scan steps. Misc: change console Docker DB path, large README/CHANGELOG updates (chat E2E, unattended access/WOL, i18n expansion, CDAP/SDK docs), and many web-nodejs assets/locales/routes/views/services and server-side changes.
150 lines
4.9 KiB
Markdown
150 lines
4.9 KiB
Markdown
# Building a CDAP Agent
|
|
|
|
This guide walks through building a custom CDAP agent that connects to a BetterDesk server.
|
|
|
|
## Prerequisites
|
|
|
|
- BetterDesk server running with CDAP enabled (`--cdap` flag or `CDAP_ENABLED=true`)
|
|
- API key created via `POST /api/keys` or the web console
|
|
- WebSocket library for your language
|
|
|
|
## Quick Start (Go)
|
|
|
|
The reference agent is at `betterdesk-agent/`. It demonstrates the full lifecycle:
|
|
|
|
```bash
|
|
cd betterdesk-agent
|
|
go build -o betterdesk-agent .
|
|
./betterdesk-agent \
|
|
--server ws://your-server:21122/cdap \
|
|
--auth-method api_key \
|
|
--api-key YOUR_KEY \
|
|
--device-id MY-DEVICE-001 \
|
|
--device-name "Office Workstation" \
|
|
--device-type os_agent
|
|
```
|
|
|
|
## Quick Start (Python)
|
|
|
|
Using the BetterDesk Python SDK:
|
|
|
|
```bash
|
|
pip install betterdesk-cdap # or: pip install -e sdks/python/
|
|
```
|
|
|
|
```python
|
|
import asyncio
|
|
from betterdesk_cdap import CDAPBridge, Widget
|
|
|
|
class MyAgent(CDAPBridge):
|
|
def __init__(self):
|
|
super().__init__(
|
|
server_url="ws://your-server:21122/cdap",
|
|
device_id="MY-PY-AGENT",
|
|
device_name="Python Agent",
|
|
device_type="custom_agent",
|
|
auth_method="api_key",
|
|
api_key="YOUR_KEY"
|
|
)
|
|
# Define widgets
|
|
self.add_widget(Widget.gauge("temp", "Temperature", group="Sensors",
|
|
unit="°C", min_val=0, max_val=100,
|
|
danger=80, warning=60))
|
|
self.add_widget(Widget.button("restart", "Restart Service",
|
|
group="Actions", command="restart_svc",
|
|
confirm=True))
|
|
|
|
async def on_connected(self):
|
|
print("Connected to server!")
|
|
|
|
async def on_command(self, command_id, command, args):
|
|
if command == "restart_svc":
|
|
# Execute the command
|
|
import subprocess
|
|
subprocess.run(["systemctl", "restart", "myservice"])
|
|
await self.send_command_response(command_id, True, "Service restarted")
|
|
else:
|
|
await self.send_command_response(command_id, False, f"Unknown: {command}")
|
|
|
|
async def collect_metrics(self):
|
|
"""Called every heartbeat interval."""
|
|
import psutil
|
|
return {
|
|
"temp": read_temperature_sensor(),
|
|
}
|
|
|
|
asyncio.run(MyAgent().run())
|
|
```
|
|
|
|
## Quick Start (Node.js)
|
|
|
|
```bash
|
|
npm install betterdesk-cdap # or: npm install ./sdks/nodejs/
|
|
```
|
|
|
|
```javascript
|
|
const { CDAPBridge, Widget } = require('betterdesk-cdap');
|
|
|
|
const agent = new CDAPBridge({
|
|
serverUrl: 'ws://your-server:21122/cdap',
|
|
deviceId: 'MY-NODE-AGENT',
|
|
deviceName: 'Node.js Agent',
|
|
deviceType: 'custom_agent',
|
|
authMethod: 'api_key',
|
|
apiKey: 'YOUR_KEY'
|
|
});
|
|
|
|
agent.addWidget(Widget.gauge('cpu', 'CPU Usage', {
|
|
group: 'System', unit: '%', min: 0, max: 100, danger: 90
|
|
}));
|
|
|
|
agent.on('command', async (cmdId, command, args) => {
|
|
if (command === 'ping') {
|
|
agent.sendCommandResponse(cmdId, true, 'pong');
|
|
}
|
|
});
|
|
|
|
agent.on('connected', () => console.log('Connected!'));
|
|
|
|
agent.connect();
|
|
```
|
|
|
|
## Agent Lifecycle
|
|
|
|
1. **Connect** — Establish WebSocket to `ws://server:21122/cdap`
|
|
2. **Authenticate** — Send `auth` message, wait for `auth_response`
|
|
3. **Register manifest** — Send device info, capabilities, and widget definitions
|
|
4. **Heartbeat loop** — Periodically send metrics and widget values
|
|
5. **Handle commands** — Listen for `command` messages, execute, respond
|
|
6. **Reconnect** — On disconnect, exponential backoff reconnect (2s, 4s, 8s... max 60s)
|
|
|
|
## Widget Best Practices
|
|
|
|
- Use **gauge** for numeric metrics with known ranges (CPU, temperature, battery)
|
|
- Use **toggle** for on/off controls (enable/disable service, relay switch)
|
|
- Use **button** with `confirm: true` for destructive actions (reboot, clear data)
|
|
- Use **text** for string values (hostname, IP address, firmware version)
|
|
- Group related widgets with the `group` field for organized display
|
|
- Keep widget IDs stable across reconnects (don't generate random IDs)
|
|
|
|
## Capabilities
|
|
|
|
Only declare capabilities your agent actually supports:
|
|
|
|
| Capability | When to declare |
|
|
|------------|----------------|
|
|
| `telemetry` | Agent collects and reports system/sensor metrics |
|
|
| `commands` | Agent can execute named commands |
|
|
| `terminal` | Agent provides PTY terminal access |
|
|
| `file_transfer` | Agent supports file browse/read/write/delete |
|
|
| `clipboard` | Agent can get/set system clipboard |
|
|
| `screenshot` | Agent can capture screen |
|
|
| `audio` | Agent supports audio streaming |
|
|
|
|
## Security Notes
|
|
|
|
- **Never hardcode API keys** in source code. Use environment variables or config files.
|
|
- **Validate all commands** before executing. Reject unknown commands.
|
|
- **Path traversal protection** — Validate file paths in file_transfer operations. The reference agent uses `safePath()` to prevent `../../` attacks.
|
|
- **Rate limiting** — Don't flood the server with state updates. Batch changes with `bulk_update`.
|