- Added Go-based Wails application for RUSTLE UCXL browser - Implemented basic application structure and configuration - Added project documentation and setup instructions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
438 lines
12 KiB
Go
438 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
|
)
|
|
|
|
// App struct
|
|
type App struct {
|
|
ctx context.Context
|
|
}
|
|
|
|
// Envelope represents a UCXL content envelope
|
|
type Envelope struct {
|
|
UCXLURI string `json:"ucxl_uri"`
|
|
Content Content `json:"content"`
|
|
Metadata map[string]string `json:"metadata"`
|
|
Timestamp string `json:"timestamp"`
|
|
Version string `json:"version"`
|
|
ContentHash string `json:"content_hash"`
|
|
}
|
|
|
|
// Content represents the actual content data
|
|
type Content struct {
|
|
Raw string `json:"raw"`
|
|
ContentType string `json:"content_type"`
|
|
Encoding string `json:"encoding"`
|
|
}
|
|
|
|
// UCXL Address parsing types and functions
|
|
type UCXLAddress struct {
|
|
Agent string `json:"agent"`
|
|
Role string `json:"role"`
|
|
Project string `json:"project"`
|
|
Task string `json:"task"`
|
|
Temporal string `json:"temporal"`
|
|
Path string `json:"path"`
|
|
Raw string `json:"raw"`
|
|
}
|
|
|
|
// Regular expression for UCXL address parsing
|
|
var ucxlAddressPattern = regexp.MustCompile(`^ucxl://([^:]+):([^@]+)@([^:]+):([^/]+)/([^/]+)/?(.*)$`)
|
|
|
|
// ParseUCXL parses a UCXL address string
|
|
func ParseUCXL(address string) (*UCXLAddress, error) {
|
|
if address == "" {
|
|
return nil, fmt.Errorf("address cannot be empty")
|
|
}
|
|
|
|
// Normalize the address
|
|
normalized := strings.TrimSpace(address)
|
|
if !strings.HasPrefix(strings.ToLower(normalized), "ucxl://") {
|
|
return nil, fmt.Errorf("address must start with 'ucxl://'")
|
|
}
|
|
|
|
// Use regex for parsing
|
|
matches := ucxlAddressPattern.FindStringSubmatch(normalized)
|
|
if matches == nil || len(matches) != 7 {
|
|
return nil, fmt.Errorf("address format must be 'ucxl://agent:role@project:task/temporal_segment/path'")
|
|
}
|
|
|
|
return &UCXLAddress{
|
|
Agent: strings.ToLower(strings.TrimSpace(matches[1])),
|
|
Role: strings.ToLower(strings.TrimSpace(matches[2])),
|
|
Project: strings.ToLower(strings.TrimSpace(matches[3])),
|
|
Task: strings.ToLower(strings.TrimSpace(matches[4])),
|
|
Temporal: strings.TrimSpace(matches[5]),
|
|
Path: matches[6], // Path can be empty
|
|
Raw: address,
|
|
}, nil
|
|
}
|
|
|
|
// String returns the canonical string representation
|
|
func (addr *UCXLAddress) String() string {
|
|
if addr.Path != "" {
|
|
return fmt.Sprintf("ucxl://%s:%s@%s:%s/%s/%s", addr.Agent, addr.Role, addr.Project, addr.Task, addr.Temporal, addr.Path)
|
|
}
|
|
return fmt.Sprintf("ucxl://%s:%s@%s:%s/%s", addr.Agent, addr.Role, addr.Project, addr.Task, addr.Temporal)
|
|
}
|
|
|
|
// NewApp creates a new App application struct
|
|
func NewApp() *App {
|
|
return &App{}
|
|
}
|
|
|
|
// startup is called when the app starts. The context is saved
|
|
// so we can call the runtime methods
|
|
func (a *App) startup(ctx context.Context) {
|
|
a.ctx = ctx
|
|
|
|
// Log startup
|
|
runtime.LogInfo(ctx, "RUSTLE started with BZZZ integration (mock mode)")
|
|
}
|
|
|
|
// GetUCXLContent retrieves content by UCXL address
|
|
func (a *App) GetUCXLContent(uri string) (map[string]interface{}, error) {
|
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Fetching UCXL content: %s", uri))
|
|
|
|
// Parse the UCXL address
|
|
addr, err := ParseUCXL(uri)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid UCXL address: %w", err)
|
|
}
|
|
|
|
// For now, return mock content based on the address
|
|
envelope, err := a.generateMockContent(addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate content: %w", err)
|
|
}
|
|
|
|
// Convert to map for JSON serialization
|
|
result := map[string]interface{}{
|
|
"ucxl_uri": envelope.UCXLURI,
|
|
"content": envelope.Content,
|
|
"metadata": envelope.Metadata,
|
|
"timestamp": envelope.Timestamp,
|
|
"version": envelope.Version,
|
|
"content_hash": envelope.ContentHash,
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// PostUCXLContent stores content at UCXL address
|
|
func (a *App) PostUCXLContent(uri string, content string, contentType string, metadata map[string]string) error {
|
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Storing UCXL content: %s", uri))
|
|
|
|
// Parse the UCXL address
|
|
addr, err := ParseUCXL(uri)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid UCXL address: %w", err)
|
|
}
|
|
|
|
// Create envelope
|
|
envelope := &Envelope{
|
|
UCXLURI: addr.String(),
|
|
Content: Content{
|
|
Raw: content,
|
|
ContentType: contentType,
|
|
Encoding: "utf-8",
|
|
},
|
|
Metadata: metadata,
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
Version: "1.0",
|
|
ContentHash: fmt.Sprintf("sha256:%x", []byte(content)), // Simple hash for demo
|
|
}
|
|
|
|
// In a real implementation, this would store to the DHT
|
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Mock stored content: %d bytes", len(content)))
|
|
|
|
// For demo purposes, we'll just log the operation
|
|
envelopeJSON, _ := json.MarshalIndent(envelope, "", " ")
|
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Stored envelope: %s", string(envelopeJSON)))
|
|
|
|
return nil
|
|
}
|
|
|
|
// SearchUCXLContent searches for content matching a pattern
|
|
func (a *App) SearchUCXLContent(query string, tags []string, limit int) ([]map[string]interface{}, error) {
|
|
runtime.LogInfo(a.ctx, fmt.Sprintf("Searching UCXL content: query=%s, tags=%v, limit=%d", query, tags, limit))
|
|
|
|
// For demo purposes, generate some mock search results
|
|
results := make([]map[string]interface{}, 0, limit)
|
|
|
|
// Generate a few mock results based on the query
|
|
for i := 0; i < min(limit, 3); i++ {
|
|
mockURI := fmt.Sprintf("ucxl://search:result@%s:demo/*^/result_%d", strings.ToLower(query), i+1)
|
|
addr, err := ParseUCXL(mockURI)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
envelope, err := a.generateMockContent(addr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
result := map[string]interface{}{
|
|
"ucxl_uri": envelope.UCXLURI,
|
|
"content": envelope.Content,
|
|
"metadata": envelope.Metadata,
|
|
"timestamp": envelope.Timestamp,
|
|
"version": envelope.Version,
|
|
"content_hash": envelope.ContentHash,
|
|
}
|
|
|
|
results = append(results, result)
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
// ValidateUCXLAddress validates a UCXL address format
|
|
func (a *App) ValidateUCXLAddress(uri string) map[string]interface{} {
|
|
result := map[string]interface{}{
|
|
"valid": false,
|
|
"error": "",
|
|
"components": map[string]string{},
|
|
}
|
|
|
|
addr, err := ParseUCXL(uri)
|
|
if err != nil {
|
|
result["error"] = err.Error()
|
|
return result
|
|
}
|
|
|
|
result["valid"] = true
|
|
result["components"] = map[string]string{
|
|
"agent": addr.Agent,
|
|
"role": addr.Role,
|
|
"project": addr.Project,
|
|
"task": addr.Task,
|
|
"temporal": addr.Temporal,
|
|
"path": addr.Path,
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// GetBZZZStatus returns the status of BZZZ DHT connection
|
|
func (a *App) GetBZZZStatus() map[string]interface{} {
|
|
status := map[string]interface{}{
|
|
"connected": false,
|
|
"mode": "mock",
|
|
"peers": 0,
|
|
"agent": "rustle",
|
|
"role": "browser",
|
|
"project": "ucxl-content-browser",
|
|
"capabilities": []string{"content-retrieval", "content-storage", "search", "address-validation"},
|
|
}
|
|
|
|
return status
|
|
}
|
|
|
|
// generateMockContent creates mock content based on UCXL address components
|
|
func (a *App) generateMockContent(addr *UCXLAddress) (*Envelope, error) {
|
|
var content string
|
|
var contentType string
|
|
|
|
// Generate different content types based on the path
|
|
switch {
|
|
case strings.HasSuffix(addr.Path, ".md"):
|
|
content = a.generateMarkdownContent(addr)
|
|
contentType = "text/markdown"
|
|
case strings.HasSuffix(addr.Path, ".json"):
|
|
content = a.generateJSONContent(addr)
|
|
contentType = "application/json"
|
|
case strings.HasSuffix(addr.Path, ".go") || strings.HasSuffix(addr.Path, ".rs") || strings.HasSuffix(addr.Path, ".js"):
|
|
content = a.generateCodeContent(addr)
|
|
contentType = "text/x-" + filepath.Ext(addr.Path)[1:]
|
|
default:
|
|
content = a.generateDefaultContent(addr)
|
|
contentType = "text/plain"
|
|
}
|
|
|
|
return &Envelope{
|
|
UCXLURI: addr.String(),
|
|
Content: Content{
|
|
Raw: content,
|
|
ContentType: contentType,
|
|
Encoding: "utf-8",
|
|
},
|
|
Metadata: map[string]string{
|
|
"title": fmt.Sprintf("%s/%s Content", strings.Title(addr.Agent), strings.Title(addr.Role)),
|
|
"author": addr.Agent,
|
|
"tags": fmt.Sprintf("%s,%s,%s", addr.Project, addr.Task, addr.Role),
|
|
"source": "rustle-mock-generator",
|
|
},
|
|
Timestamp: time.Now().Format(time.RFC3339),
|
|
Version: "1.0",
|
|
ContentHash: fmt.Sprintf("sha256:%x", []byte(content)),
|
|
}, nil
|
|
}
|
|
|
|
func (a *App) generateMarkdownContent(addr *UCXLAddress) string {
|
|
return fmt.Sprintf(`# %s/%s - %s
|
|
|
|
This is mock content generated for the UCXL address: %s
|
|
|
|
## Project Context
|
|
- **Project**: %s
|
|
- **Task**: %s
|
|
- **Agent**: %s
|
|
- **Role**: %s
|
|
|
|
## Content Details
|
|
- **Path**: %s
|
|
- **Temporal**: %s
|
|
|
|
This content demonstrates the RUSTLE browser's ability to render Markdown content from UCXL addresses.
|
|
|
|
### Features
|
|
- ✅ UCXL address parsing
|
|
- ✅ Content type detection
|
|
- ✅ Markdown rendering
|
|
- ✅ BZZZ DHT integration (mock mode)
|
|
|
|
### Integration Notes
|
|
This content is being served through the Wails desktop application with Go backend and React frontend.
|
|
`, strings.Title(addr.Agent), strings.Title(addr.Role), strings.Title(addr.Task),
|
|
addr.String(), addr.Project, addr.Task, addr.Agent, addr.Role, addr.Path, addr.Temporal)
|
|
}
|
|
|
|
func (a *App) generateJSONContent(addr *UCXLAddress) string {
|
|
data := map[string]interface{}{
|
|
"ucxl_address": addr.String(),
|
|
"components": map[string]string{
|
|
"agent": addr.Agent,
|
|
"role": addr.Role,
|
|
"project": addr.Project,
|
|
"task": addr.Task,
|
|
"path": addr.Path,
|
|
"temporal": addr.Temporal,
|
|
},
|
|
"mock_data": map[string]interface{}{
|
|
"generated_at": time.Now().Format(time.RFC3339),
|
|
"generator": "rustle-wails-backend",
|
|
"content_type": "application/json",
|
|
"capabilities": []string{
|
|
"content-rendering",
|
|
"address-parsing",
|
|
"mock-generation",
|
|
},
|
|
},
|
|
"bzzz_integration": map[string]interface{}{
|
|
"mode": "mock",
|
|
"agent": "rustle",
|
|
"role": "browser",
|
|
"project": "ucxl-content-browser",
|
|
},
|
|
}
|
|
|
|
jsonData, _ := json.MarshalIndent(data, "", " ")
|
|
return string(jsonData)
|
|
}
|
|
|
|
func (a *App) generateCodeContent(addr *UCXLAddress) string {
|
|
ext := filepath.Ext(addr.Path)
|
|
switch ext {
|
|
case ".go":
|
|
return fmt.Sprintf(`package %s
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
)
|
|
|
|
// %sHandler handles %s operations
|
|
type %sHandler struct {
|
|
ctx context.Context
|
|
}
|
|
|
|
// New%sHandler creates a new handler
|
|
func New%sHandler(ctx context.Context) *%sHandler {
|
|
return &%sHandler{ctx: ctx}
|
|
}
|
|
|
|
// Process handles the %s task
|
|
func (h *%sHandler) Process() error {
|
|
log.Printf("Processing %s task for project %s")
|
|
return nil
|
|
}
|
|
`, addr.Task, strings.Title(addr.Role), addr.Task, strings.Title(addr.Role),
|
|
strings.Title(addr.Role), strings.Title(addr.Role), strings.Title(addr.Role),
|
|
strings.Title(addr.Role), addr.Task, strings.Title(addr.Role), addr.Task, addr.Project)
|
|
|
|
case ".js":
|
|
return fmt.Sprintf(`// %s/%s - %s
|
|
// UCXL Address: %s
|
|
|
|
class %sHandler {
|
|
constructor() {
|
|
this.project = '%s';
|
|
this.task = '%s';
|
|
this.agent = '%s';
|
|
this.role = '%s';
|
|
}
|
|
|
|
async process() {
|
|
console.log('Processing task: ' + this.task + ' for project: ' + this.project);
|
|
// Mock implementation
|
|
return {
|
|
status: 'success',
|
|
ucxl_address: '%s',
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = %sHandler;
|
|
`, strings.Title(addr.Agent), strings.Title(addr.Role), strings.Title(addr.Task), addr.String(),
|
|
strings.Title(addr.Role), addr.Project, addr.Task, addr.Agent, addr.Role, addr.String(), strings.Title(addr.Role))
|
|
|
|
default:
|
|
return fmt.Sprintf("// Mock code content for %s\n// Generated from UCXL address: %s\n", addr.Path, addr.String())
|
|
}
|
|
}
|
|
|
|
func (a *App) generateDefaultContent(addr *UCXLAddress) string {
|
|
return fmt.Sprintf(`UCXL Content - %s
|
|
|
|
Address: %s
|
|
Agent: %s
|
|
Role: %s
|
|
Project: %s
|
|
Task: %s
|
|
Path: %s
|
|
Temporal: %s
|
|
|
|
This is mock content generated by the RUSTLE browser's BZZZ-integrated backend.
|
|
Generated at: %s
|
|
|
|
Content demonstrates successful UCXL address parsing and mock content generation.
|
|
`, strings.Title(addr.Task), addr.String(), addr.Agent, addr.Role,
|
|
addr.Project, addr.Task, addr.Path, addr.Temporal,
|
|
time.Now().Format(time.RFC3339))
|
|
}
|
|
|
|
// Helper function for min
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Greet returns a greeting for the given name (legacy function)
|
|
func (a *App) Greet(name string) string {
|
|
return fmt.Sprintf("Hello %s, Welcome to RUSTLE - UCXL Content Browser!", name)
|
|
} |