...
This commit is contained in:
414
_pkg2_dont_use/clients/mycelium/cmd/main.go
Normal file
414
_pkg2_dont_use/clients/mycelium/cmd/main.go
Normal file
@@ -0,0 +1,414 @@
|
||||
// pkg/mycelium_client/cmd/main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.ourworld.tf/herocode/heroagent/pkg/mycelium_client"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
baseURL string
|
||||
command string
|
||||
peerEndpoint string
|
||||
message string
|
||||
destination string
|
||||
topic string
|
||||
timeout int
|
||||
wait bool
|
||||
replyTimeout int
|
||||
messageID string
|
||||
outputJSON bool
|
||||
}
|
||||
|
||||
// Commands
|
||||
const (
|
||||
cmdInfo = "info"
|
||||
cmdPeers = "peers"
|
||||
cmdAddPeer = "add-peer"
|
||||
cmdDelPeer = "del-peer"
|
||||
cmdSend = "send"
|
||||
cmdReceive = "receive"
|
||||
cmdReply = "reply"
|
||||
cmdStatus = "status"
|
||||
cmdRoutes = "routes"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create config with default values
|
||||
cfg := config{
|
||||
baseURL: fmt.Sprintf("http://localhost:%d", mycelium_client.DefaultAPIPort),
|
||||
timeout: 30,
|
||||
replyTimeout: mycelium_client.DefaultReplyTimeout,
|
||||
}
|
||||
|
||||
// Parse command line flags
|
||||
flag.StringVar(&cfg.baseURL, "api", cfg.baseURL, "Mycelium API URL")
|
||||
flag.IntVar(&cfg.timeout, "timeout", cfg.timeout, "Client timeout in seconds")
|
||||
flag.BoolVar(&cfg.outputJSON, "json", false, "Output in JSON format")
|
||||
flag.Parse()
|
||||
|
||||
// Get the command
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg.command = args[0]
|
||||
args = args[1:]
|
||||
|
||||
// Create client
|
||||
client := mycelium_client.NewClient(cfg.baseURL)
|
||||
client.SetTimeout(time.Duration(cfg.timeout) * time.Second)
|
||||
|
||||
// Create context with cancellation for graceful shutdowns
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Set up signal handling
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-sigCh
|
||||
fmt.Println("\nReceived interrupt signal, shutting down...")
|
||||
cancel()
|
||||
}()
|
||||
|
||||
// Execute command
|
||||
var err error
|
||||
switch cfg.command {
|
||||
case cmdInfo:
|
||||
err = showNodeInfo(ctx, client)
|
||||
|
||||
case cmdPeers:
|
||||
err = listPeers(ctx, client, cfg.outputJSON)
|
||||
|
||||
case cmdAddPeer:
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Missing peer endpoint argument")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.peerEndpoint = args[0]
|
||||
err = addPeer(ctx, client, cfg.peerEndpoint)
|
||||
|
||||
case cmdDelPeer:
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Missing peer endpoint argument")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.peerEndpoint = args[0]
|
||||
err = removePeer(ctx, client, cfg.peerEndpoint)
|
||||
|
||||
case cmdSend:
|
||||
parseMessageArgs(&cfg, args)
|
||||
err = sendMessage(ctx, client, cfg)
|
||||
|
||||
case cmdReceive:
|
||||
parseReceiveArgs(&cfg, args)
|
||||
err = receiveMessage(ctx, client, cfg)
|
||||
|
||||
case cmdReply:
|
||||
parseReplyArgs(&cfg, args)
|
||||
err = replyToMessage(ctx, client, cfg)
|
||||
|
||||
case cmdStatus:
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Missing message ID argument")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.messageID = args[0]
|
||||
err = getMessageStatus(ctx, client, cfg.messageID)
|
||||
|
||||
case cmdRoutes:
|
||||
var routeType string
|
||||
if len(args) > 0 {
|
||||
routeType = args[0]
|
||||
}
|
||||
err = listRoutes(ctx, client, routeType, cfg.outputJSON)
|
||||
|
||||
default:
|
||||
fmt.Printf("Unknown command: %s\n", cfg.command)
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Println("Usage: mycelium-client [flags] COMMAND [args...]")
|
||||
fmt.Println("\nFlags:")
|
||||
flag.PrintDefaults()
|
||||
fmt.Println("\nCommands:")
|
||||
fmt.Println(" info Get node information")
|
||||
fmt.Println(" peers List connected peers")
|
||||
fmt.Println(" add-peer ENDPOINT Add a new peer")
|
||||
fmt.Println(" del-peer ENDPOINT Remove a peer")
|
||||
fmt.Println(" send [--pk=PK|--ip=IP] [--topic=TOPIC] [--wait] [--reply-timeout=N] MESSAGE")
|
||||
fmt.Println(" Send a message to a destination")
|
||||
fmt.Println(" receive [--topic=TOPIC] [--timeout=N]")
|
||||
fmt.Println(" Receive a message")
|
||||
fmt.Println(" reply ID [--topic=TOPIC] MESSAGE")
|
||||
fmt.Println(" Reply to a message")
|
||||
fmt.Println(" status ID Get status of a sent message")
|
||||
fmt.Println(" routes [selected|fallback] List routes (default: selected)")
|
||||
}
|
||||
|
||||
func parseMessageArgs(cfg *config, args []string) {
|
||||
// Create a temporary flag set
|
||||
fs := flag.NewFlagSet("send", flag.ExitOnError)
|
||||
fs.StringVar(&cfg.destination, "pk", "", "Destination public key (hex encoded)")
|
||||
fs.StringVar(&cfg.destination, "ip", "", "Destination IP address")
|
||||
fs.StringVar(&cfg.topic, "topic", "", "Message topic")
|
||||
fs.BoolVar(&cfg.wait, "wait", false, "Wait for reply")
|
||||
fs.IntVar(&cfg.replyTimeout, "reply-timeout", cfg.replyTimeout, "Reply timeout in seconds")
|
||||
|
||||
// Parse args
|
||||
fs.Parse(args)
|
||||
|
||||
// Remaining args are the message
|
||||
remainingArgs := fs.Args()
|
||||
if len(remainingArgs) == 0 {
|
||||
fmt.Println("Missing message content")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.message = strings.Join(remainingArgs, " ")
|
||||
}
|
||||
|
||||
func parseReceiveArgs(cfg *config, args []string) {
|
||||
// Create a temporary flag set
|
||||
fs := flag.NewFlagSet("receive", flag.ExitOnError)
|
||||
fs.StringVar(&cfg.topic, "topic", "", "Message topic filter")
|
||||
fs.IntVar(&cfg.timeout, "timeout", 10, "Receive timeout in seconds")
|
||||
|
||||
// Parse args
|
||||
fs.Parse(args)
|
||||
}
|
||||
|
||||
func parseReplyArgs(cfg *config, args []string) {
|
||||
if len(args) < 1 {
|
||||
fmt.Println("Missing message ID argument")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg.messageID = args[0]
|
||||
args = args[1:]
|
||||
|
||||
// Create a temporary flag set
|
||||
fs := flag.NewFlagSet("reply", flag.ExitOnError)
|
||||
fs.StringVar(&cfg.topic, "topic", "", "Message topic")
|
||||
|
||||
// Parse args
|
||||
fs.Parse(args)
|
||||
|
||||
// Remaining args are the message
|
||||
remainingArgs := fs.Args()
|
||||
if len(remainingArgs) == 0 {
|
||||
fmt.Println("Missing reply message content")
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
cfg.message = strings.Join(remainingArgs, " ")
|
||||
}
|
||||
|
||||
func showNodeInfo(ctx context.Context, client *mycelium_client.MyceliumClient) error {
|
||||
info, err := client.GetNodeInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Node Information:")
|
||||
fmt.Printf(" Subnet: %s\n", info.NodeSubnet)
|
||||
return nil
|
||||
}
|
||||
|
||||
func listPeers(ctx context.Context, client *mycelium_client.MyceliumClient, jsonOutput bool) error {
|
||||
peers, err := client.ListPeers(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
// TODO: Output JSON
|
||||
fmt.Printf("Found %d peers\n", len(peers))
|
||||
} else {
|
||||
fmt.Printf("Connected Peers (%d):\n", len(peers))
|
||||
if len(peers) == 0 {
|
||||
fmt.Println(" No peers connected")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, peer := range peers {
|
||||
fmt.Printf(" %d. %s://%s\n", i+1, peer.Endpoint.Proto, peer.Endpoint.SocketAddr)
|
||||
fmt.Printf(" Type: %s, State: %s\n", peer.Type, peer.ConnectionState)
|
||||
if peer.TxBytes > 0 || peer.RxBytes > 0 {
|
||||
fmt.Printf(" TX: %d bytes, RX: %d bytes\n", peer.TxBytes, peer.RxBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addPeer(ctx context.Context, client *mycelium_client.MyceliumClient, endpoint string) error {
|
||||
if err := client.AddPeer(ctx, endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Peer added: %s\n", endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
func removePeer(ctx context.Context, client *mycelium_client.MyceliumClient, endpoint string) error {
|
||||
if err := client.RemovePeer(ctx, endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Peer removed: %s\n", endpoint)
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendMessage(ctx context.Context, client *mycelium_client.MyceliumClient, cfg config) error {
|
||||
var dst mycelium_client.MessageDestination
|
||||
|
||||
if cfg.destination == "" {
|
||||
return fmt.Errorf("destination is required (--pk or --ip)")
|
||||
}
|
||||
|
||||
// Determine destination type
|
||||
if strings.HasPrefix(cfg.destination, "--pk=") {
|
||||
dst.PK = strings.TrimPrefix(cfg.destination, "--pk=")
|
||||
} else if strings.HasPrefix(cfg.destination, "--ip=") {
|
||||
dst.IP = strings.TrimPrefix(cfg.destination, "--ip=")
|
||||
} else {
|
||||
// Try to guess format
|
||||
if strings.Contains(cfg.destination, ":") {
|
||||
dst.IP = cfg.destination
|
||||
} else {
|
||||
dst.PK = cfg.destination
|
||||
}
|
||||
}
|
||||
|
||||
// Send message
|
||||
payload := []byte(cfg.message)
|
||||
reply, id, err := client.SendMessage(ctx, dst, payload, cfg.topic, cfg.wait, cfg.replyTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply != nil {
|
||||
fmt.Println("Received reply:")
|
||||
printMessage(reply)
|
||||
} else {
|
||||
fmt.Printf("Message sent successfully. ID: %s\n", id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func receiveMessage(ctx context.Context, client *mycelium_client.MyceliumClient, cfg config) error {
|
||||
fmt.Printf("Waiting for message (timeout: %d seconds)...\n", cfg.timeout)
|
||||
msg, err := client.ReceiveMessage(ctx, cfg.timeout, cfg.topic, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msg == nil {
|
||||
fmt.Println("No message received within timeout")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println("Message received:")
|
||||
printMessage(msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func replyToMessage(ctx context.Context, client *mycelium_client.MyceliumClient, cfg config) error {
|
||||
if err := client.ReplyToMessage(ctx, cfg.messageID, []byte(cfg.message), cfg.topic); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Reply sent to message ID: %s\n", cfg.messageID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMessageStatus(ctx context.Context, client *mycelium_client.MyceliumClient, messageID string) error {
|
||||
status, err := client.GetMessageStatus(ctx, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Message Status (ID: %s):\n", messageID)
|
||||
for k, v := range status {
|
||||
fmt.Printf(" %s: %v\n", k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func listRoutes(ctx context.Context, client *mycelium_client.MyceliumClient, routeType string, jsonOutput bool) error {
|
||||
var routes []mycelium_client.Route
|
||||
var err error
|
||||
|
||||
// Default to selected routes
|
||||
if routeType == "" || routeType == "selected" {
|
||||
routes, err = client.ListSelectedRoutes(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Selected Routes (%d):\n", len(routes))
|
||||
} else if routeType == "fallback" {
|
||||
routes, err = client.ListFallbackRoutes(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Fallback Routes (%d):\n", len(routes))
|
||||
} else {
|
||||
return fmt.Errorf("unknown route type: %s (use 'selected' or 'fallback')", routeType)
|
||||
}
|
||||
|
||||
if jsonOutput {
|
||||
// TODO: Output JSON
|
||||
fmt.Printf("Found %d routes\n", len(routes))
|
||||
} else {
|
||||
if len(routes) == 0 {
|
||||
fmt.Println(" No routes found")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, route := range routes {
|
||||
fmt.Printf(" %d. Subnet: %s\n", i+1, route.Subnet)
|
||||
fmt.Printf(" Next Hop: %s\n", route.NextHop)
|
||||
fmt.Printf(" Metric: %v, Sequence: %d\n", route.Metric, route.Seqno)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func printMessage(msg *mycelium_client.InboundMessage) {
|
||||
payload, err := msg.Decode()
|
||||
fmt.Printf(" ID: %s\n", msg.ID)
|
||||
fmt.Printf(" From: %s (IP: %s)\n", msg.SrcPK, msg.SrcIP)
|
||||
fmt.Printf(" To: %s (IP: %s)\n", msg.DstPK, msg.DstIP)
|
||||
if msg.Topic != "" {
|
||||
fmt.Printf(" Topic: %s\n", msg.Topic)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf(" Payload (base64): %s\n", msg.Payload)
|
||||
fmt.Printf(" Error decoding payload: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf(" Payload: %s\n", string(payload))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user