@delve.sh/plugin-sdk
v1.0.6
Published
Delve Plugin SDK for creating custom plugins
Readme
@portablesheep/plugin-sdk
Utilities for building Delve plugins. This repository ships two packages that work together:
- Go module
github.com/PortableSheep/delve-sdkfor authoring plugin backends - npm package
@delve/plugin-sdkfor frontend command bridging
The sections below describe what each package provides today.
SDK Versioning & Schemas
- The current Go SDK version is exposed as
sdk.Version(e.g.v0.5.0). Plugins should include this value in their registration payloads so the host can reject incompatible builds early. sdk.CompatibleHostRangedocuments the Delve host versions the SDK is tested with. Future releases will expand this range; treat it as the source of truth when shipping plugins.- JSON schema helpers
sdk.PluginManifestSchema()andsdk.LayoutSchema()are available for validatingplugin.jsonmanifests and persisted docking layout state. These are the same schemas the Delve host enforces at runtime.
Frontend command bridge (npm)
Install from GitHub Packages
- go-utility: Full-featured Go plugin with frontend
- theme: CSS theme plugin
- integration: API integration plugin
- dashboard: Data visualization plugin
Custom Templates
customTemplate := &sdk.PluginTemplate{
Name: "custom-api",
Description: "Custom API integration template",
Type: "integration",
Language: "go",
Files: []sdk.TemplateFile{
{
Path: "main.go",
Template: true,
Content: "// Custom template content with {{.Variables}}",
},
},
Variables: map[string]interface{}{
"APIVersion": "v1",
},
}
enhancedSDK.Generator().RegisterTemplate(customTemplate)🔧 Configuration
SDK Configuration
config := sdk.SDKConfig{
RegistryURL: "https://registry.delve.sh",
SecurityLevel: sdk.SecurityLevelMedium,
CacheEnabled: true,
DevMode: false,
LogLevel: sdk.LogLevelInfo,
CustomTemplates: map[string]*sdk.PluginTemplate{
"my-template": customTemplate,
},
}
sdk := sdk.NewEnhancedSDK(config)📚 API Reference
Core Types
EnhancedSDK- Main SDK interfaceEnhancedPlugin- Enhanced plugin interface with lifecycle methodsSecurityManager- Handles security policies and sandboxingEnhancedRegistry- Advanced plugin registry with search and metadataSchemaValidator- Plugin configuration validationPluginGenerator- Template-based plugin generationDevTools- Development and build tools
Security Types
SecurityLevel- Security level enumerationSecurityPolicy- Security policy configurationPermission- Permission request structureSandbox- Isolated execution environment
Registry Types
EnhancedPluginInfo- Complete plugin metadataRegistrySearchFilter- Advanced search filteringPluginMetrics- Usage and performance metrics
🤝 Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'Add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
- Issues: GitHub Issues
Storage API
Plugin.SetItem(key, value string) error
Store a key-value pair in plugin storage.
Plugin.GetItem(key string) (string, error)
Retrieve a value from plugin storage.
Plugin.DeleteItem(key string) error
Delete a key from plugin storage.
Plugin.ListItems() ([]string, error)
List all keys in plugin storage.
Plugin.ClearStorage() error
Clear all plugin storage.
Heartbeat Behavior
The heartbeat system provides robust connection monitoring:
Normal Operation
- Plugin sends heartbeat every
intervalseconds - Host responds with heartbeat acknowledgment
- Plugin tracks last successful heartbeat response
Failure Detection
- If no heartbeat response received within
timeout - Plugin logs timeout warning
- Calls
StateManager.SaveState()if configured - Plugin shuts down gracefully
Benefits
- Prevents zombie processes when host crashes
- Saves plugin state before unexpected shutdown
- Reduces resource usage by cleaning up orphaned plugins
- Improves system stability
Error Handling
Connection Errors
plugin, err := sdk.Start(pluginInfo)
if err != nil {
log.Printf("Failed to connect: %v", err)
// Handle connection failure
}Storage Errors
err := plugin.SetItem("key", "value")
if err != nil {
log.Printf("Storage error: %v", err)
// Handle storage failure
}Best Practices
1. Always Use Heartbeat
// Recommended for all production plugins
plugin.StartHeartbeat(30*time.Second, 60*time.Second)2. Implement State Management
type MyPlugin struct {
importantData map[string]interface{}
configPath string
}
func (p *MyPlugin) SaveState() error {
// Save critical state that should survive restarts
return saveToFile(p.configPath, p.importantData)
}3. Handle Graceful Shutdown
import (
"os"
"os/signal"
"syscall"
)
// Listen for OS signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
log.Println("Shutting down gracefully...")
plugin.Close()
os.Exit(0)
}()4. Use Context for Background Work
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
// Do background work
}
}
}()5. Log Important Events
plugin.Listen(func(messageType int, data []byte) {
log.Printf("Received message type %d: %s", messageType, string(data))
// Process message
})Plugin Lifecycle
UI Contributions (Sidebar & Footer)
Plugins can extend the Delve UI with sidebar items and footer widgets, and update them live at runtime.
Helpers on Plugin:
SetUIContributions(c UIContributions)– replace all sidebar/footer entriesUpsertSidebarItem(item SidebarItem)– insert or replace a sidebar item and emit an updateUpsertFooterWidget(w FooterWidget)– insert or replace a footer widget and emit an updateSetFooterBadgeCount(identifier string, count int)– update a footer widget badge by ElementTag or Title
Minimal usage:
// During registration
pluginInfo := &sdk.RegisterRequest{
Name: "my-plugin",
Description: "Demo",
CustomElementTag: "my-plugin",
UiComponentPath: "frontend/component.js",
}
plugin, _ := sdk.Start(pluginInfo)
// Contribute a sidebar item and a footer icon
_ = plugin.SetUIContributions(sdk.UIContributions{
Sidebar: []sdk.SidebarItem{
{ Title: "My Plugin", ElementTag: "my-plugin", ComponentPath: "frontend/component.js", OnClickCommand: &sdk.CommandRef{ID: "my-plugin.open"} },
},
Footer: []sdk.FooterWidget{
{ Title: "My Plugin", Icon: "mdi:rocket", OnClickCommand: &sdk.CommandRef{ID: "my-plugin.open"} },
},
})
// Update badge later (e.g., unread count)
_ = plugin.SetFooterBadgeCount("My Plugin", 5)
// Default click handler returns an activation hint for the frontend
plugin.OnCommand("my-plugin.open", func(ctx context.Context, args []any) (any, error) {
return map[string]any{"activateUI": map[string]any{"plugin": "my-plugin", "tag": "my-plugin"}}, nil
})- Startup: Plugin connects and registers with host
- Running: Plugin processes messages and performs work
- Heartbeat: Regular health checks with host
- Shutdown: Graceful cleanup when connection lost or timeout
- State Save: Critical data persisted before exit
Publishing to Registry
To make your plugin available through the Delve Plugin Registry:
- Create plugin.json with complete metadata including screenshots
- Add screenshots to a
screenshots/directory in your repository - Submit to registry via pull request
Directory Structure:
my-plugin/
├── plugin.json # Plugin metadata
├── main.go # Plugin source code
├── frontend/
│ └── component.js # Frontend component
├── screenshots/ # Plugin screenshots
├── overview.png
├── settings.png
└── features.pngFrontend Integration
Your plugin's frontend component should match the CustomElementTag:
// If CustomElementTag is "my-plugin"
class MyPlugin extends HTMLElement {
connectedCallback() {
this.innerHTML = '<h1>My Plugin UI</h1>';
}
}
customElements.define('my-plugin', MyPlugin);Troubleshooting
Plugin Not Appearing in Sidebar
- Check host-config.yml has plugin enabled
- Verify plugin executable exists and is executable
- Check plugin registration logs
Connection Issues
- Ensure host is running on correct WebSocket port
- Check firewall settings
- Verify --ws-port flag is passed correctly
Heartbeat Timeouts
- Check host system resources
- Adjust timeout values for slower systems
- Monitor network connectivity
State Not Saving
- Verify StateManager interface implementation
- Check file permissions for state files
- Review error logs during shutdown
Examples
See example.go for a complete plugin implementation with:
- Heartbeat monitoring
- State management
- Background work
- Graceful shutdown
- Signal handling
Requirements
- Go 1.19 or later
- Delve host application
- WebSocket support
