Compare commits

..

No commits in common. "b5ee6e061acf1388aef777ddcd9a2bf84bd6ed57" and "1a1e17fa1012e4db10a0f716c9b63a03b4863c9f" have entirely different histories.

3 changed files with 235 additions and 261 deletions

193
PROMPT.md Normal file
View file

@ -0,0 +1,193 @@
# BotApp Development Guide
**Version:** 6.2.0
**Purpose:** Desktop application wrapper (Tauri 2)
---
## ZERO TOLERANCE POLICY
**EVERY SINGLE WARNING MUST BE FIXED. NO EXCEPTIONS.**
---
## ❌ ABSOLUTE PROHIBITIONS
```
❌ NEVER use #![allow()] or #[allow()] in source code
❌ NEVER use _ prefix for unused variables - DELETE or USE them
❌ NEVER use .unwrap() - use ? or proper error handling
❌ NEVER use .expect() - use ? or proper error handling
❌ NEVER use panic!() or unreachable!()
❌ NEVER use todo!() or unimplemented!()
❌ NEVER leave unused imports or dead code
❌ NEVER add comments - code must be self-documenting
```
---
## 🔐 SECURITY - TAURI SPECIFIC
```
❌ NEVER trust user input from IPC commands
❌ NEVER expose filesystem paths to frontend without validation
❌ NEVER store secrets in plain text or localStorage
❌ NEVER disable CSP in tauri.conf.json for production
❌ NEVER use allowlist: all in Tauri configuration
```
### Path Validation
```rust
// ❌ WRONG - trusting user path
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(path).map_err(|e| e.to_string())
}
// ✅ CORRECT - validate and sandbox paths
#[tauri::command]
async fn read_file(app: tauri::AppHandle, filename: String) -> Result<String, String> {
let safe_name = filename
.chars()
.filter(|c| c.is_alphanumeric() || *c == '.' || *c == '-')
.collect::<String>();
if safe_name.contains("..") {
return Err("Invalid filename".into());
}
let base_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
let full_path = base_dir.join(&safe_name);
std::fs::read_to_string(full_path).map_err(|e| e.to_string())
}
```
---
## 🏗️ ARCHITECTURE
### Structure
```
botapp/
├── src/
│ └── main.rs # Rust backend, Tauri commands
├── ui/
│ └── app-guides/ # App-specific HTML
├── js/
│ └── app-extensions.js # JavaScript extensions
├── icons/ # App icons (all sizes)
├── tauri.conf.json # Tauri configuration
└── Cargo.toml
```
### Communication Flow
```
Native UI (HTML/CSS/JS)
↓ Tauri IPC (invoke)
Rust #[tauri::command]
↓ HTTP (reqwest)
botserver API
Business Logic + Database
```
---
## 🔧 TAURI COMMAND PATTERN
```rust
use tauri::command;
#[command]
pub async fn my_command(
window: tauri::Window,
param: String,
) -> Result<MyResponse, String> {
if param.is_empty() || param.len() > 1000 {
return Err("Invalid parameter".into());
}
Ok(MyResponse { /* ... */ })
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
my_command,
])
.run(tauri::generate_context!())
.map_err(|e| format!("error running app: {e}"))?;
}
```
### JavaScript Invocation
```javascript
const result = await window.__TAURI__.invoke('my_command', {
param: 'value'
});
```
---
## 🎨 ICONS - MANDATORY
**NEVER generate icons with LLM. Use official SVG icons from `botui/ui/suite/assets/icons/`**
Required icon sizes in `icons/`:
```
icon.ico # Windows (256x256)
icon.icns # macOS
icon.png # Linux (512x512)
32x32.png
128x128.png
128x128@2x.png
```
---
## ⚙️ CONFIGURATION (tauri.conf.json)
```json
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "General Bots",
"version": "6.2.0",
"identifier": "br.com.pragmatismo.botapp",
"build": {
"devUrl": "http://localhost:3000",
"frontendDist": "../botui/ui/suite"
},
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
}
}
}
```
---
## 📦 KEY DEPENDENCIES
| Library | Version | Purpose |
|---------|---------|---------|
| tauri | 2 | Desktop framework |
| tauri-plugin-dialog | 2 | File dialogs |
| tauri-plugin-opener | 2 | URL/file opener |
| botlib | path | Shared types |
| reqwest | 0.12 | HTTP client |
| tokio | 1.41 | Async runtime |
---
## 🔑 REMEMBER
- **ZERO WARNINGS** - Every clippy warning must be fixed
- **NO ALLOW IN CODE** - Never use #[allow()] in source files
- **NO DEAD CODE** - Delete unused code
- **NO UNWRAP/EXPECT** - Use ? operator
- **Security** - Minimal allowlist, validate ALL inputs
- **Desktop-only features** - Shared logic in botserver
- **Tauri APIs** - No direct fs access from JS
- **Version 6.2.0** - do not change without approval

295
README.md
View file

@ -1,17 +1,6 @@
# BotApp - General Bots Desktop Application
**Version:** 6.2.0
**Purpose:** Desktop application wrapper (Tauri 2)
---
## Overview
BotApp is the Tauri-based desktop wrapper for General Bots, providing native desktop and mobile capabilities on top of the pure web UI from [botui](https://github.com/GeneralBots/botui). It extends the web interface with native file system access, system tray functionality, and desktop-specific features while maintaining a clean separation from the pure web UI.
For comprehensive documentation, see **[docs.pragmatismo.com.br](https://docs.pragmatismo.com.br)** or the **[BotBook](../botbook)** for detailed guides, API references, and tutorials.
---
BotApp is the Tauri-based desktop wrapper for General Bots, providing native desktop and mobile capabilities on top of the pure web UI from [botui](https://github.com/GeneralBots/botui).
## Architecture
@ -36,20 +25,6 @@ This separation allows:
- Clean dependency management (web users don't need Tauri)
- App-specific features only in the native app
### Communication Flow
```
Native UI (HTML/CSS/JS)
↓ Tauri IPC (invoke)
Rust #[tauri::command]
↓ HTTP (reqwest)
botserver API
Business Logic + Database
```
---
## Features
BotApp adds these native capabilities to botui:
@ -60,39 +35,35 @@ BotApp adds these native capabilities to botui:
- **Desktop Notifications**: Native OS notifications
- **App Settings**: Desktop-specific configuration
---
## Project Structure
```
botapp/
├── Cargo.toml # Rust dependencies (includes Tauri)
├── build.rs # Tauri build script
├── tauri.conf.json # Tauri configuration
├── src/
│ ├── main.rs # Rust backend, Tauri commands
│ ├── lib.rs # Library exports
│ ├── main.rs # Tauri entry point
│ ├── lib.rs # Library exports
│ └── desktop/
│ ├── mod.rs # Desktop module organization
│ ├── drive.rs # File system commands
│ └── tray.rs # System tray functionality
│ ├── mod.rs # Desktop module
│ ├── drive.rs # File system commands
│ └── tray.rs # System tray functionality
├── ui/
│ └── app-guides/ # App-specific HTML
├── js/
│ └── app-extensions.js # JavaScript extensions
├── icons/ # App icons (all sizes)
├── tauri.conf.json # Tauri configuration
└── Cargo.toml
│ └── app-guides/ # App-only HTML content
│ ├── local-files.html
│ └── native-settings.html
└── js/
└── app-extensions.js # Injected into botui's suite
```
---
## Development
### Prerequisites
## Prerequisites
- Rust 1.70+
- Node.js 18+ (for Tauri CLI)
- Tauri CLI: `cargo install tauri-cli`
#### Platform-specific
### Platform-specific
**Linux:**
```bash
@ -107,7 +78,7 @@ xcode-select --install
**Windows:**
- Visual Studio Build Tools with C++ workload
### Getting Started
## Development
1. Clone both repositories:
```bash
@ -127,8 +98,6 @@ cd botapp
cargo tauri dev
```
---
## Building
### Debug Build
@ -143,134 +112,7 @@ cargo tauri build
Binaries will be in `target/release/bundle/`.
---
## 🖥️ Tauri Command Pattern
```rust
use tauri::command;
#[command]
pub async fn my_command(
window: tauri::Window,
param: String,
) -> Result<MyResponse, String> {
if param.is_empty() || param.len() > 1000 {
return Err("Invalid parameter".into());
}
Ok(MyResponse { /* ... */ })
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
my_command,
])
.run(tauri::generate_context!())
.map_err(|e| format!("error running app: {e}"))?;
}
```
### JavaScript Invocation
```javascript
const result = await window.__TAURI__.invoke('my_command', {
param: 'value'
});
```
### Available Tauri Commands
| Command | Description |
|---------|-------------|
| `list_files` | List directory contents |
| `upload_file` | Copy file with progress |
| `create_folder` | Create new directory |
| `delete_path` | Delete file or folder |
| `get_home_dir` | Get user's home directory |
---
## 🔐 Security Directives
### Path Validation
```rust
// ❌ WRONG - trusting user path
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(path).map_err(|e| e.to_string())
}
// ✅ CORRECT - validate and sandbox paths
#[tauri::command]
async fn read_file(app: tauri::AppHandle, filename: String) -> Result<String, String> {
let safe_name = filename
.chars()
.filter(|c| c.is_alphanumeric() || *c == '.' || *c == '-')
.collect::<String>();
if safe_name.contains("..") {
return Err("Invalid filename".into());
}
let base_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
let full_path = base_dir.join(&safe_name);
std::fs::read_to_string(full_path).map_err(|e| e.to_string())
}
```
### Security Prohibitions
```
❌ NEVER trust user input from IPC commands
❌ NEVER expose filesystem paths to frontend without validation
❌ NEVER store secrets in plain text or localStorage
❌ NEVER disable CSP in tauri.conf.json for production
❌ NEVER use allowlist: all in Tauri configuration
```
---
## 🎨 Icons - MANDATORY
**NEVER generate icons with LLM. Use official SVG icons from `botui/ui/suite/assets/icons/`**
Required icon sizes in `icons/`:
```
icon.ico # Windows (256x256)
icon.icns # macOS
icon.png # Linux (512x512)
32x32.png
128x128.png
128x128@2x.png
```
All icons use `stroke="currentColor"` for CSS theming.
---
## ⚙️ Configuration (tauri.conf.json)
```json
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "General Bots",
"version": "6.2.0",
"identifier": "br.com.pragmatismo.botapp",
"build": {
"devUrl": "http://localhost:3000",
"frontendDist": "../botui/ui/suite"
},
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
}
}
}
```
---
## 🎯 How App Extensions Work
## How App Extensions Work
BotApp injects `js/app-extensions.js` into botui's suite at runtime. This script:
@ -287,63 +129,34 @@ if (window.BotApp?.isApp) {
}
```
---
## Tauri Commands
## ✅ ZERO TOLERANCE POLICY
Available Tauri commands (invokable from JS):
**EVERY SINGLE WARNING MUST BE FIXED. NO EXCEPTIONS.**
| Command | Description |
|---------|-------------|
| `list_files` | List directory contents |
| `upload_file` | Copy file with progress |
| `create_folder` | Create new directory |
| `delete_path` | Delete file or folder |
| `get_home_dir` | Get user's home directory |
### Absolute Prohibitions
## Configuration
```
❌ NEVER use #![allow()] or #[allow()] in source code
❌ NEVER use _ prefix for unused variables - DELETE or USE them
❌ NEVER use .unwrap() - use ? or proper error handling
❌ NEVER use .expect() - use ? or proper error handling
❌ NEVER use panic!() or unreachable!()
❌ NEVER use todo!() or unimplemented!()
❌ NEVER leave unused imports or dead code
❌ NEVER add comments - code must be self-documenting
```
Edit `tauri.conf.json` to customize:
### Code Patterns
- `productName`: Application name
- `identifier`: Unique app identifier
- `build.devUrl`: URL for development (default: `http://localhost:3000`)
- `build.frontendDist`: Path to botui's UI (default: `../botui/ui/suite`)
```rust
// ❌ WRONG
let value = something.unwrap();
## License
// ✅ CORRECT
let value = something?;
let value = something.ok_or_else(|| Error::NotFound)?;
AGPL-3.0 - See [LICENSE](LICENSE) for details.
// Use Self in Impl Blocks
impl MyStruct {
fn new() -> Self { Self { } } // ✅ Not MyStruct
}
## Testing and Safety Tooling
// Derive Eq with PartialEq
#[derive(PartialEq, Eq)] // ✅ Always both
struct MyStruct { }
```
---
## 📦 Key Dependencies
| Library | Version | Purpose |
|---------|---------|---------|
| tauri | 2 | Desktop framework |
| tauri-plugin-dialog | 2 | File dialogs |
| tauri-plugin-opener | 2 | URL/file opener |
| botlib | workspace | Shared types |
| reqwest | 0.12 | HTTP client |
| tokio | 1.41 | Async runtime |
---
## 🧪 Testing and Safety Tooling
BotApp follows General Bots' commitment to code quality and safety.
BotApp follows General Bots' commitment to code quality and safety. The following tools are available for verification:
### Standard Testing
@ -388,40 +201,10 @@ Ferrocene is a qualified Rust compiler for safety-critical systems (ISO 26262, I
For most use cases, comprehensive testing with the tools above provides adequate confidence.
---
See [Testing & Safety Tooling](../botbook/src/07-gbapp/testing-safety.md) for complete documentation.
## 📚 Documentation
For complete documentation, guides, and API references:
- **[docs.pragmatismo.com.br](https://docs.pragmatismo.com.br)** - Full online documentation
- **[BotBook](../botbook)** - Local comprehensive guide with tutorials and examples
- **[Testing & Safety Tooling](../botbook/src/07-gbapp/testing-safety.md)** - Complete testing documentation
---
## 🔑 Remember
- **ZERO WARNINGS** - Every clippy warning must be fixed
- **NO ALLOW IN CODE** - Never use #[allow()] in source files
- **NO DEAD CODE** - Delete unused code
- **NO UNWRAP/EXPECT** - Use ? operator
- **Security** - Minimal allowlist, validate ALL inputs
- **Desktop-only features** - Shared logic in botserver
- **Tauri APIs** - No direct fs access from JS
- **Official icons** - Use icons from botui/ui/suite/assets/icons/
- **Version 6.2.0** - Do not change without approval
---
## 🔗 Related Projects
## Related Projects
- [botui](https://github.com/GeneralBots/botui) - Pure web UI
- [botserver](https://github.com/GeneralBots/botserver) - Backend server
- [botlib](https://github.com/GeneralBots/botlib) - Shared Rust library
---
## License
AGPL-3.0 - See [LICENSE](LICENSE) for details.

View file

@ -13,10 +13,7 @@ async fn get_tray_status(tray: tauri::State<'_, TrayManager>) -> Result<bool, St
}
#[tauri::command]
async fn start_tray(
tray: tauri::State<'_, TrayManager>,
app: tauri::AppHandle,
) -> Result<(), String> {
async fn start_tray(tray: tauri::State<'_, TrayManager>, app: tauri::AppHandle) -> Result<(), String> {
tray.start(&app).await.map_err(|e| e.to_string())
}
@ -161,6 +158,7 @@ fn create_tray_with_mode(mode: String) -> Result<String, String> {
fn main() {
botlib::logging::init_compact_logger("info");
let version = env!("CARGO_PKG_VERSION");
info!("BotApp {version} starting...");