Enable devnet-builder to passthrough commands to active plugin binaries, allowing users to execute commands like:
devnet-builder stabled status
devnet-builder stabled tx bank send ...This design follows Clean Architecture and SOLID principles.
BinaryCommand: Represents a command to be passed to a binary- BinaryName: string
- Args: []string
- WorkDir: string
-
BinaryResolver: Interface for resolving binary paths from plugin namestype BinaryResolver interface { ResolveBinary(ctx context.Context, pluginName string) (binaryPath string, err error) GetActiveBinary(ctx context.Context) (binaryPath string, pluginName string, err error) }
-
BinaryExecutor: Interface for executing binary commandstype BinaryExecutor interface { Execute(ctx context.Context, cmd BinaryCommand) error }
type ExecuteBinaryPassthroughUseCase struct {
resolver BinaryResolver
exportExec BinaryExecutor
cache BinaryCache
}
func (uc *ExecuteBinaryPassthroughUseCase) Execute(ctx context.Context, binaryName string, args []string) errorResponsibilities:
- Resolve the binary name to an actual binary path
- Validate the binary exists and is executable
- Execute the binary with provided arguments
- Stream output to user
Adapts plugin system to BinaryResolver interface:
type PluginBinaryResolver struct {
loader *plugin.Loader
cache ports.BinaryCache
networkModule network.Module
}Responsibilities:
- Query plugin loader for available plugins
- Extract binary name from plugin Module interface
- Check if plugin binary exists in cache
- Return full path to cached binary
Adapts existing ProcessExecutor to BinaryExecutor:
type ProcessBinaryExecutor struct {
exportExec ports.ProcessExecutor
}func NewBinaryPassthroughCmd(container *di.Container) *cobra.CommandCommand Routing Logic:
- Check if first argument matches a known plugin binary name
- If yes, route to binary passthrough handler
- If no, continue with normal command handling
Modify root.go to:
- Discover available plugins at startup
- Create dynamic subcommands for each plugin binary
- Route unknown commands to passthrough handler
// In NewRootCmd()
func enhanceWithBinaryPassthrough(rootCmd *cobra.Command, container *di.Container) {
// Discover plugins
plugins := discoverPluginBinaries(container)
// Add dynamic commands
for _, pluginName := range plugins {
passthroughCmd := createPassthroughCommand(pluginName, container)
rootCmd.AddCommand(passthroughCmd)
}
}- Define domain interfaces (BinaryResolver, BinaryExecutor)
- Implement PluginBinaryResolver
- Implement ProcessBinaryExecutor
- Implement ExecuteBinaryPassthroughUseCase
- Add validation and error handling
- Add logging and telemetry
- Create binary passthrough command handler
- Integrate with root command
- Add command completion support
- Unit tests for resolvers and executors
- Integration tests with mock plugins
- E2E tests with actual plugin binaries
BinaryResolver: Only resolves binary pathsBinaryExecutor: Only executes binariesExecuteBinaryPassthroughUseCase: Orchestrates the passthrough flow
- Interfaces allow extension without modification
- New binary sources can be added by implementing BinaryResolver
- All BinaryResolver implementations are interchangeable
- All BinaryExecutor implementations are interchangeable
- Small, focused interfaces (BinaryResolver, BinaryExecutor)
- Clients depend only on methods they use
- High-level use cases depend on abstractions (interfaces)
- Low-level implementations depend on abstractions
- DI container manages dependencies
- Plugin Not Found: Return clear error with available plugins
- Binary Not Cached: Suggest running build/download first
- Binary Not Executable: Check permissions and report
- Execution Failure: Stream stderr and return exit code
- Validate plugin binaries are in expected directories
- Check binary signatures/checksums (future enhancement)
- Prevent path traversal attacks
- Limit execution to whitelisted plugins
# Get active binary from config
devnet-builder stabled status
# Explicit plugin specification
devnet-builder --blockchain-network stable stabled tx bank send ...
# With passthrough to full binary
devnet-builder stabled query bank balances cosmos1...Add to config.toml:
[binary_passthrough]
enabled = true
allow_all_plugins = true # or whitelist specific plugins
whitelisted_plugins = ["stable", "ault"]