cmdutils

package
v0.0.0-...-2c4dd60 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2022 License: MIT Imports: 47 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	CmdMnemonic = REPLCommand{
		HelpText: "show your identity's mnemonic",
		Handler: func(args []string, app *App) error {
			m, err := app.KeyStore.Mnemonic()
			if err != nil {
				return err
			}
			app.Debugf("mnemonic: %v", m)
			return nil
		},
	}

	CmdAddress = REPLCommand{
		HelpText: "show your address",
		Handler: func(args []string, app *App) error {
			identity, err := app.KeyStore.DefaultPublicIdentity()
			if err != nil {
				return err
			}
			app.Debugf("address: %v", identity.Address())
			return nil
		},
	}

	CmdLibp2pPeerID = REPLCommand{
		HelpText: "show your libp2p peer ID",
		Handler: func(args []string, app *App) error {
			if app.Libp2pTransport == nil {
				return errors.New("libp2p is disabled")
			}
			peerID := app.Libp2pTransport.Libp2pPeerID()
			app.Debugf("libp2p peer ID: %v", peerID)
			return nil
		},
	}

	CmdLibp2pRelayAdd = REPLCommand{
		HelpText: "add a libp2p static relay",
		Handler: func(args []string, app *App) error {
			if app.Libp2pTransport == nil {
				return errors.New("libp2p is disabled")
			} else if len(args) < 1 {
				return errors.New("requires 1 argument: libp2p relay add <multiaddress>")
			}
			return app.Libp2pTransport.AddStaticRelay(args[0])
		},
	}

	CmdLibp2pRelayRemove = REPLCommand{
		HelpText: "remove a libp2p static relay",
		Handler: func(args []string, app *App) error {
			if app.Libp2pTransport == nil {
				return errors.New("libp2p is disabled")
			} else if len(args) < 1 {
				return errors.New("requires 1 argument: libp2p relay rm <multiaddress>")
			}
			return app.Libp2pTransport.RemoveStaticRelay(args[0])
		},
	}

	CmdLibp2pRelayList = REPLCommand{
		HelpText: "list the currently configured libp2p static relays",
		Handler: func(args []string, app *App) error {
			if app.Libp2pTransport == nil {
				return errors.New("libp2p is disabled")
			}
			for _, relay := range app.Libp2pTransport.StaticRelays().Slice() {
				fmt.Println(" -", relay)
			}
			return nil
		},
	}

	CmdLibp2pStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the libp2p store",
		Handler: func(args []string, app *App) error {
			app.Libp2pStore.DebugPrint()
			return nil
		},
	}

	CmdSubscribe = REPLCommand{
		HelpText: "subscribe to a state URI",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("missing argument: state URI")
			}

			stateURI := args[0]

			err := app.TreeProto.Subscribe(context.Background(), stateURI)
			if err != nil {
				return err
			}
			return nil
		},
	}

	CmdStateURIs = REPLCommand{
		HelpText: "list all known state URIs",
		Handler: func(args []string, app *App) error {
			stateURIs, err := app.ControllerHub.StateURIsWithData()
			if err != nil {
				return err
			}
			if len(stateURIs) == 0 {
				fmt.Println("no known state URIs")
			} else {
				for stateURI := range stateURIs {
					fmt.Println("- ", stateURI)
				}
			}
			return nil
		},
	}

	CmdGetState = REPLCommand{
		HelpText: "print the current state tree for a state URI",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("missing argument: state URI")
			}

			stateURI := args[0]
			node, err := app.ControllerHub.StateAtVersion(stateURI, nil)
			if err != nil {
				return err
			}
			var keypath state.Keypath
			var rng *state.Range
			if len(args) > 1 {
				keypath, rng, err = state.ParseKeypathAndRange([]byte(args[1]), byte('.'))
				if err != nil {
					return err
				}
			}
			app.Debugf("stateURI: %v / keypath: %v / range: %v", stateURI, keypath, rng)
			node = node.NodeAt(keypath, rng)
			node.DebugPrint(app.Debugf, false, 0)
			fmt.Println(utils.PrettyJSON(node))
			return nil
		},
	}

	CmdSetState = REPLCommand{
		HelpText: "set a keypath in a state tree",
		Handler: func(args []string, app *App) error {
			if len(args) < 3 {
				return errors.New("requires 3 arguments: set <state URI> <keypath> <JSON value>")
			}
			stateURI := args[0]
			keypath := state.Keypath(args[1])
			jsonVal := strings.Join(args[2:], " ")

			_, err := app.ControllerHub.StateAtVersion(stateURI, nil)
			var txID state.Version
			if errors.Cause(err) == errors.Err404 {
				txID = tree.GenesisTxID
			} else {
				txID = state.RandomVersion()
			}

			err = app.TreeProto.SendTx(context.TODO(), tree.Tx{
				ID:       txID,
				StateURI: stateURI,
				Patches: []tree.Patch{{
					Keypath:   keypath,
					ValueJSON: []byte(jsonVal),
				}},
			})
			return err
		},
	}

	CmdListTxs = REPLCommand{
		HelpText: "list the txs for a given state URI",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("requires 1 arguments: txs <state URI>")
			}
			stateURI := args[0]

			iter, err := app.TxStore.AllTxsForStateURI(stateURI, tree.GenesisTxID)
			if err != nil {
				return err
			}

			var rows [][]string
			for iter.Rewind(); iter.Valid(); iter.Next() {
				tx := iter.Tx()
				if tx == nil {
					break
				}
				var parents []string
				for _, parent := range tx.Parents {
					parents = append(parents, parent.Hex())
				}
				rows = append(rows, []string{tx.ID.Hex(), tx.Status.String(), strings.Join(parents, " ")})
			}
			if iter.Err() != nil {
				app.Errorf("iterator: %v", iter.Err())
			}

			table := tablewriter.NewWriter(os.Stdout)
			table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
			table.SetCenterSeparator("|")
			table.SetRowLine(true)
			table.SetAutoMergeCellsByColumnIndex([]int{0, 1})
			table.SetHeader([]string{"ID", "Status", "Parents"})
			table.AppendBulk(rows)
			table.Render()
			return nil
		},
	}

	CmdTxStoreLeaves = REPLCommand{
		HelpText: "print the leaves of the given state URI",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("requires 1 argument: leaves <state URI>")
			}
			leaves, err := app.TxStore.Leaves(args[0])
			if err != nil {
				return err
			}
			for _, leaf := range leaves {
				app.Infof(0, "- %v", leaf.Hex())
			}
			return nil
		},
	}

	CmdTxStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the tx store",
		Handler: func(args []string, app *App) error {
			app.TxStore.DebugPrint()
			return nil
		},
	}

	CmdBlobs = REPLCommand{
		HelpText: "list all blobs",
		Handler: func(args []string, app *App) error {
			app.BlobStore.DebugPrint()

			contents, err := app.BlobStore.Contents()
			if err != nil {
				return err
			}

			var rows [][]string

			for blobHash, x := range contents {
				for chunkHash, have := range x {
					rows = append(rows, []string{blobHash.Hex(), chunkHash.Hex(), fmt.Sprintf("%v", have)})
				}
			}

			table := tablewriter.NewWriter(os.Stdout)
			table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
			table.SetCenterSeparator("|")
			table.SetRowLine(true)
			table.SetAutoMergeCellsByColumnIndex([]int{0, 1})
			table.SetHeader([]string{"Blob", "Chunk", "Have"})
			table.AppendBulk(rows)
			table.Render()

			return nil
		},
	}

	CmdPeers = REPLCommand{
		HelpText: "list all known peers",
		Handler: func(args []string, app *App) error {
			var full bool
			if len(args) > 0 {
				if args[0] == "full" {
					full = true
				} else {
					return errors.Errorf("unknown argument '%v'", args[0])
				}
			}

			fmtPeerRow := func(addr, duID, dialAddr string, lastContact, lastFailure time.Time, failures uint64, remainingBackoff time.Duration, stateURIs []string) []string {
				if !full && len(addr) > 10 {
					addr = addr[:4] + "..." + addr[len(addr)-4:]
				}
				if !full && len(dialAddr) > 30 {
					dialAddr = dialAddr[:30] + "..." + dialAddr[len(dialAddr)-6:]
				}
				lastContactStr := time.Now().Sub(lastContact).Round(1 * time.Second).String()
				if lastContact.IsZero() {
					lastContactStr = ""
				}
				lastFailureStr := time.Now().Sub(lastFailure).Round(1 * time.Second).String()
				if lastFailure.IsZero() {
					lastFailureStr = ""
				}
				failuresStr := fmt.Sprintf("%v", failures)
				remainingBackoffStr := remainingBackoff.Round(1 * time.Second).String()
				if remainingBackoff == 0 {
					remainingBackoffStr = ""
				}
				if !full && len(duID) > 4 {
					duID = duID[:4] + "..."
				}
				return []string{addr, duID, dialAddr, lastContactStr, lastFailureStr, failuresStr, remainingBackoffStr, fmt.Sprintf("%v", stateURIs)}
			}

			var data [][]string
			for _, peer := range app.PeerStore.Peers() {
				for _, e := range peer.Endpoints() {
					for addr := range peer.Addresses() {
						data = append(data, fmtPeerRow(addr.Hex(), e.DeviceUniqueID(), e.DialInfo().DialAddr, e.LastContact(), e.LastFailure(), e.Failures(), e.RemainingBackoff(), e.StateURIs().Slice()))
					}
					if len(e.Addresses()) == 0 {
						data = append(data, fmtPeerRow("?", e.DeviceUniqueID(), e.DialInfo().DialAddr, e.LastContact(), e.LastFailure(), e.Failures(), e.RemainingBackoff(), e.StateURIs().Slice()))
					}
				}
			}

			sort.Slice(data, func(i, j int) bool {
				cmp := strings.Compare(data[i][0], data[j][0])
				if cmp == 0 {
					return strings.Compare(data[i][1], data[j][1]) < 0
				} else {
					return cmp < 0
				}
			})

			table := tablewriter.NewWriter(os.Stdout)
			table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
			table.SetCenterSeparator("|")
			table.SetRowLine(true)
			table.SetHeader([]string{"Address", "DeviceID", "DialAddr", "LastContact", "LastFailure", "Failures", "Backoff", "StateURIs"})
			table.SetAutoMergeCellsByColumnIndex([]int{0, 1})
			table.SetColumnColor(
				tablewriter.Colors{tablewriter.Bold, tablewriter.FgCyanColor},
				tablewriter.Colors{},
				tablewriter.Colors{},
				tablewriter.Colors{},
				tablewriter.Colors{},
				tablewriter.Colors{},
				tablewriter.Colors{},
				tablewriter.Colors{},
			)
			table.AppendBulk(data)
			table.Render()

			return nil
		},
	}

	CmdAddPeer = REPLCommand{
		HelpText: "add a peer",
		Handler: func(args []string, app *App) error {
			if len(args) < 2 {
				return errors.New("requires 2 arguments: peers add <transport> <dial addr>")
			}
			app.PeerStore.AddDialInfo(swarm.PeerDialInfo{args[0], args[1]}, "")
			return nil
		},
	}

	CmdRemoveAllPeers = REPLCommand{
		HelpText: "remove all peers",
		Handler: func(args []string, app *App) error {
			var toDelete []string
			for _, peer := range app.PeerStore.Peers() {
				toDelete = append(toDelete, peer.DeviceUniqueID())
			}
			app.PeerStore.RemovePeers(toDelete)
			return nil
		},
	}

	CmdRemoveUnverifiedPeers = REPLCommand{
		HelpText: "remove peers who haven't been verified",
		Handler: func(args []string, app *App) error {
			var toDelete []string
			for _, peer := range app.PeerStore.Peers() {
				if len(peer.Addresses()) == 0 {
					toDelete = append(toDelete, peer.DeviceUniqueID())
				}
			}
			app.PeerStore.RemovePeers(toDelete)
			return nil
		},
	}

	CmdRemoveFailedPeers = REPLCommand{
		HelpText: "remove peers with more than a certain number of failures",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("requires 1 argument: rmfailedpeers <number of failures>")
			}

			num, err := strconv.Atoi(args[0])
			if err != nil {
				return errors.Wrap(err, "bad argument")
			}

			var toDelete []string
			for _, peer := range app.PeerStore.Peers() {
				if peer.Failures() > uint64(num) {
					toDelete = append(toDelete, peer.DeviceUniqueID())
				}
			}
			app.PeerStore.RemovePeers(toDelete)
			return nil
		},
	}

	CmdHushSendIndividualMessage = REPLCommand{
		HelpText: "send a 1:1 Hush message",
		Handler: func(args []string, app *App) error {
			if len(args) < 2 {
				return errors.New("requires 2 arguments: hushmsg <recipient address> <message>")
			}

			recipient, err := types.AddressFromHex(args[0])
			if err != nil {
				return err
			}
			msg := strings.Join(args[1:], " ")

			return app.HushProto.EncryptIndividualMessage("foo", recipient, []byte(msg))
		},
	}

	CmdHushSendGroupMessage = REPLCommand{
		HelpText: "send a group Hush message",
		Handler: func(args []string, app *App) error {
			if len(args) < 2 {
				return errors.New("requires 2 arguments: hushmsg <comma-separated recipient addresses> <message>")
			}

			addrStrs := strings.Split(args[0], ",")
			var recipients []types.Address
			for _, s := range addrStrs {
				addr, err := types.AddressFromHex(s)
				if err != nil {
					return err
				}
				recipients = append(recipients, addr)
			}

			msg := strings.Join(args[1:], " ")

			id := types.RandomID()
			return app.HushProto.EncryptGroupMessage("foo", id.Hex(), recipients, []byte(msg))
		},
	}

	CmdHushStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the protohush store",
		Handler: func(args []string, app *App) error {
			app.HushProtoStore.DebugPrint()
			return nil
		},
	}

	CmdTreeStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the prototree store",
		Handler: func(args []string, app *App) error {
			app.TreeProtoStore.DebugPrint()
			return nil
		},
	}

	CmdControllerDebugPrint = REPLCommand{
		HelpText: "print the contents of a state DB",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("requires 1 argument: tree dumptree <state uri>")
			}
			app.ControllerHub.DebugPrint(args[0])
			return nil
		},
	}

	CmdPeerStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the peer store",
		Handler: func(args []string, app *App) error {
			app.PeerStore.DebugPrint()
			return nil
		},
	}

	CmdProcessTree = REPLCommand{
		HelpText: "display the current process tree",
		Handler: func(args []string, app *App) error {
			app.Infof(0, "processes:\n%v", utils.PrettyJSON(app.ProcessTree()))
			return nil
		},
	}

	CmdSetBlobMaxFetchConns = REPLCommand{
		HelpText: "set the maximum number of peers to fetch a blob from simultaneously",
		Handler: func(args []string, app *App) error {
			if len(args) < 1 {
				return errors.New("requires 1 argument: blob set maxfetchconns <n>")
			}
			n, err := strconv.ParseUint(args[0], 10, 64)
			if err != nil {
				return err
			}
			return app.BlobStore.SetMaxFetchConns(n)
		},
	}

	CmdSharedStateStoreDebugPrint = REPLCommand{
		HelpText: "print the contents of the shared state store",
		Handler: func(args []string, app *App) error {
			node := app.SharedStateDB.State(false)
			defer node.Close()
			node.DebugPrint(func(msg string, args ...interface{}) { fmt.Printf(msg, args...) }, true, 0)
			return nil
		},
	}
)
View Source
var ErrShowHelp = errors.New("")

Functions

func AppendDigits

func AppendDigits(inValue int, buf []byte) int

AppendDigits appends the base 10 value to the given buffer, returning the number of bytes written.

Pre: inValue > 0

func AppendNDigits

func AppendNDigits(inNumDigits int, inValue int, buf []byte, inPad byte) int

AppendNDigits formats an n-digit integer to the given buffer, padding as needed, returning the number of bytes written.

Pre: len(buf) >= inNumDigits

func AppendTimestamp

func AppendTimestamp(buf []byte) int

AppendTimestamp appends a glog/klog-style timestamp to the given slice, returning how many bytes were written.

Pre: len(buf) >= 20

func AwaitInterrupt

func AwaitInterrupt() <-chan struct{}

func DefaultConfigPath

func DefaultConfigPath(appName string) (root string, _ error)

func DefaultConfigRoot

func DefaultConfigRoot(appName string) (root string, _ error)

func DefaultDataRoot

func DefaultDataRoot(appName string) (string, error)

func FindOrCreateConfigAtPath

func FindOrCreateConfigAtPath(dst *Config, appName, configPath string) error

func NewTermUI

func NewTermUI() *termUI

Types

type App

type App struct {
	process.Process
	log.Logger

	Config Config
	TermUI *termUI

	ControllerHub       tree.ControllerHub
	TxStore             tree.TxStore
	KeyStore            identity.KeyStore
	PeerStore           swarm.PeerStore
	BlobStore           blob.Store
	AuthProto           protoauth.AuthProtocol
	BlobProto           protoblob.BlobProtocol
	HushProto           protohush.HushProtocol
	HushProtoStore      protohush.Store
	TreeProto           prototree.TreeProtocol
	TreeProtoStore      prototree.Store
	HTTPTransport       braidhttp.Transport
	Libp2pTransport     libp2p.Transport
	Libp2pStore         libp2p.Store
	HTTPRPCServer       *http.Server
	HTTPRPCServerConfig rpc.HTTPConfig
	SharedStateDB       *state.DBTree

	PeerDB *state.DBTree
	HushDB *state.DBTree
	TreeDB *state.DBTree

	Nurse *health.Nurse
}

func NewApp

func NewApp(name string, config Config) *App

func (*App) Close

func (app *App) Close() error

func (*App) EnsureDataDirs

func (a *App) EnsureDataDirs(config Config) error

func (*App) EnsureInitialState

func (app *App) EnsureInitialState(stateURI string, checkKeypath string, value interface{})

func (*App) Start

func (app *App) Start() error

type AuthProtocolConfig

type AuthProtocolConfig struct {
	Enabled bool `yaml:"Enabled"`
}

type BlobProtocolConfig

type BlobProtocolConfig struct {
	Enabled bool `yaml:"Enabled"`
}

type BootstrapPeer

type BootstrapPeer struct {
	Transport     string   `yaml:"Transport"`
	DialAddresses []string `yaml:"DialAddresses"`
}

type BraidHTTPTransportConfig

type BraidHTTPTransportConfig struct {
	Enabled         bool   `yaml:"Enabled"`
	ListenHost      string `yaml:"ListenHost"`
	ListenHostSSL   string `yaml:"ListenHostSSL"`
	TLSCertFile     string `yaml:"TLSCertFile"`
	TLSKeyFile      string `yaml:"TLSKeyFile"`
	DefaultStateURI string `yaml:"DefaultStateURI"`
	ReachableAt     string `yaml:"ReachableAt"`
}

type Component

type Component interface {
	ui.Drawable
	Update()
	HandleInput(evt ui.Event) bool
	Component() ui.Drawable
	NeedsRefresh() <-chan struct{}
}

type Config

type Config struct {
	Mode            Mode            `yaml:"-"`
	REPLConfig      REPLConfig      `yaml:"-"`
	BootstrapPeers  []BootstrapPeer `yaml:"BootstrapPeers"`
	DataRoot        string          `yaml:"DataRoot"`
	DNSOverHTTPSURL string          `yaml:"DNSOverHTTPSURL"`
	JWTSecret       string          `yaml:"JWTSecret"`
	DevMode         bool            `yaml:"-"`
	Nurse           NurseConfig     `yaml:"Nurse"`
	KeyStore        KeyStoreConfig  `yaml:"-"`
	PprofPort       uint32          `yaml:"PprofPort"`

	Libp2pTransport    Libp2pTransportConfig    `yaml:"Libp2pTransport"`
	BraidHTTPTransport BraidHTTPTransportConfig `yaml:"BraidHTTPTransport"`

	AuthProtocol AuthProtocolConfig `yaml:"AuthProtocol"`
	BlobProtocol BlobProtocolConfig `yaml:"BlobProtocol"`
	HushProtocol HushProtocolConfig `yaml:"HushProtocol"`
	TreeProtocol TreeProtocolConfig `yaml:"TreeProtocol"`

	HTTPRPC *rpc.HTTPConfig `yaml:"HTTPRPC"`
	// contains filtered or unexported fields
}

func DefaultConfig

func DefaultConfig(appName string) Config

func (*Config) BlobDataRoot

func (c *Config) BlobDataRoot() string

func (*Config) KeyStoreRoot

func (c *Config) KeyStoreRoot() string

func (*Config) Save

func (c *Config) Save() error

func (*Config) StateDBRoot

func (c *Config) StateDBRoot() string

func (*Config) TxDBRoot

func (c *Config) TxDBRoot() string

type FmtConstWidth

type FmtConstWidth struct {
	// FileNameCharWidth is the number of chars to use from the given file name.
	// Filenames shorter than this are padded with spaces.
	// If 0, file names are not printed.
	FileNameCharWidth int

	// If set, color codes will be inserted
	UseColor bool
}

FmtConstWidth is a basic formatter that makes reasonable attempts to make the header length a constant width, improving readability. It also can insert console color codes so each severity level is a different color.

func (*FmtConstWidth) FormatHeader

func (f *FmtConstWidth) FormatHeader(inSeverity string, inFile string, inLine int, buf *bytes.Buffer)

FormatHeader -- see interface Formatter

type HushProtocolConfig

type HushProtocolConfig struct {
	Enabled bool `yaml:"Enabled"`
}

type KeyStoreConfig

type KeyStoreConfig struct {
	Password             string `yaml:"-"`
	Mnemonic             string `yaml:"-"`
	InsecureScryptParams bool   `yaml:"-"`
}

type Libp2pTransportConfig

type Libp2pTransportConfig struct {
	Enabled      bool     `yaml:"Enabled"`
	ListenAddr   string   `yaml:"ListenAddr"`
	ListenPort   uint     `yaml:"ListenPort"`
	Key          string   `yaml:"Key"`
	Reachability string   `yaml:"Reachability"`
	ReachableAt  string   `yaml:"ReachableAt"`
	StaticRelays []string `yaml:"StaticRelays"`
}

type Mode

type Mode int
const (
	ModeREPL Mode = iota
	ModeTermUI
	ModeHeadless
)

func (*Mode) UnmarshalText

func (m *Mode) UnmarshalText(bs []byte) error

type NurseConfig

type NurseConfig struct {
	Enabled              bool           `yaml:"Enabled"`
	ProfileRoot          string         `yaml:"ProfileRoot"`
	PollInterval         time.Duration  `yaml:"PollInterval"`
	GatherDuration       time.Duration  `yaml:"GatherDuration"`
	MaxProfileSize       utils.FileSize `yaml:"MaxProfileSize"`
	CPUProfileRate       int            `yaml:"CPUProfileRate"`
	MemProfileRate       int            `yaml:"MemProfileRate"`
	BlockProfileRate     int            `yaml:"BlockProfileRate"`
	MutexProfileFraction int            `yaml:"MutexProfileFraction"`
	MemThreshold         utils.FileSize `yaml:"MemThreshold"`
	GoroutineThreshold   int            `yaml:"GoroutineThreshold"`
}

type REPLCommand

type REPLCommand struct {
	HelpText    string
	Subcommands REPLCommands
	Handler     func(args []string, app *App) error
}

func (REPLCommand) Handle

func (c REPLCommand) Handle(cmdParts []string, args []string, app *App) error

func (REPLCommand) Help

func (c REPLCommand) Help() string

type REPLCommands

type REPLCommands map[string]REPLCommand

func (REPLCommands) Handle

func (c REPLCommands) Handle(cmdParts []string, args []string, app *App) error

func (REPLCommands) Help

func (c REPLCommands) Help() string

type REPLConfig

type REPLConfig struct {
	Prompt   string
	Commands REPLCommands
}

type REPLHandler

type REPLHandler interface {
	Handle(args []string, app *App) error
	Help() string
}

type TreeProtocolConfig

type TreeProtocolConfig struct {
	Enabled                 bool   `yaml:"Enabled"`
	MaxPeersPerSubscription uint64 `yaml:"MaxPeersPerSubscription"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL