cli

package
v0.0.0-...-1233b70 Latest Latest
Warning

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

Go to latest
Published: Jun 20, 2026 License: AGPL-3.0 Imports: 38 Imported by: 0

Documentation

Overview

Package cli provides a unified interactive TUI for config editing, CLI, and SSH sessions.

Design: docs/architecture/config/yang-config-design.md — config editor Detail: editor_draft.go — write-through draft protocol (sub-hub for commit/walk) Detail: editor_session.go — session identity for concurrent editing Detail: editor_annotated.go — annotated view and show column preferences

Package editor provides an interactive configuration editor.

Design: docs/architecture/config/yang-config-design.md — config editor write operations Overview: editor.go — editor state and lifecycle

Index

Constants

View Source
const (
	FmtTree   = fmtTree
	FmtConfig = fmtConfig

	SrcSaved     = srcSaved
	SrcConfirmed = srcConfirmed
	CmpRollback  = cmdRollback
)

Show format and source names for pipes and version display.

Variables

View Source
var (
	ErrLeafAlreadyInactive = errors.New("leaf already inactive")
	ErrLeafNotInactive     = errors.New("leaf is not inactive")
	ErrPathAlreadyInactive = errors.New("path already inactive")
	ErrPathNotInactive     = errors.New("path is not inactive")
	ErrPathNotFound        = errors.New("path not found")
)

Sentinel errors returned by the leaf and path deactivation helpers. Callers (the CLI verb, the TUI command) use errors.Is to distinguish "no change" from real failures so they can decide whether to surface or swallow the result.

View Source
var (
	ConflictLive  = contract.ConflictLive
	ConflictStale = contract.ConflictStale
)

Re-export contract conflict constants for backward compatibility.

Functions

func ApplyPipeFilter

func ApplyPipeFilter(content string, filter PipeFilter) (string, error)

ApplyPipeFilter applies a single pipe filter to content. Returns error for unknown filter types.

func ChangePath

func ChangePath(configPath, user string) string

ChangePath returns the per-user change file path for a given config path and user. Uses filepath.Base on the user to strip directory traversal.

func ChangePrefix

func ChangePrefix(configPath string) string

ChangePrefix returns the filename prefix for scanning all change files. Used with store.List(dir) to filter change files from other files.

func ChangeUser

func ChangeUser(configPath, changeFilePath string) string

ChangeUser extracts the username from a change file path. Returns empty string if the path is not a valid change file.

func DraftPath

func DraftPath(configPath string) string

DraftPath returns the draft file path for a given config path (appends ".draft").

func FindPipeIndex

func FindPipeIndex(tokens []string) int

findPipeIndex returns the index of "|" in tokens, or -1 if not found.

func LockPath

func LockPath(configPath string) string

LockPath returns the lock file path for a given config path (appends ".lock").

func NormalizeCompareTarget

func NormalizeCompareTarget(target string) string

NormalizeCompareTarget maps compare aliases to the shared target names used by SSH and web CLI compare handling.

func TranscriptEnabled

func TranscriptEnabled() bool

TranscriptEnabled returns true if the ze.cli.transcript env var is set to a truthy value.

func ValidateUser

func ValidateUser(user string) error

ValidateUser checks whether a user string is safe for use as a change file identifier. Only alphanumeric characters, hyphens, underscores, and dots are allowed. Returns an error for empty strings, "..", or any character outside the whitelist.

func WrapExecutorWithTranscript

func WrapExecutorWithTranscript(fn func(string) (string, error), tw *TranscriptWriter) func(string) (string, error)

WrapExecutorWithTranscript wraps a command executor function so that every command and its response are recorded in the transcript. The original response is returned unchanged.

Types

type BackupInfo

type BackupInfo struct {
	Path      string
	Timestamp time.Time
}

BackupInfo describes a backup file.

type CommandCompleter

type CommandCompleter struct {
	// contains filtered or unexported fields
}

CommandCompleter delegates to command.TreeCompleter and converts command.Suggestion to the editor's Completion type at the boundary.

func NewCommandCompleter

func NewCommandCompleter(root *command.Node) *CommandCompleter

NewCommandCompleter creates a completer from a command tree root.

func (*CommandCompleter) Complete

func (c *CommandCompleter) Complete(input string) []Completion

Complete returns completions for the given input.

func (*CommandCompleter) GhostText

func (c *CommandCompleter) GhostText(input string) string

GhostText returns the best single completion for inline display.

func (*CommandCompleter) SetActiveBackends

func (c *CommandCompleter) SetActiveBackends(backends map[string]string)

SetActiveBackends propagates per-component backend names to the tree completer.

type CommandModeCompleter

type CommandModeCompleter interface {
	Complete(input string) []Completion
	GhostText(input string) string
}

CommandModeCompleter provides completions for command mode. Implemented by CommandCompleter (operational commands) and PluginCompleter (plugin SDK methods).

type CommandNode

type CommandNode = command.Node

CommandNode is an alias for command.Node. Use command.Node directly in new code.

type CommitResult

type CommitResult = contract.CommitResult

CommitResult holds the outcome of a CommitSession attempt. CommitResult is a type alias of contract.CommitResult.

type Completer

type Completer struct {
	// contains filtered or unexported fields
}

Completer provides YANG-driven completions.

func NewCompleter

func NewCompleter() *Completer

NewCompleter creates a completer using YANG schema.

func (*Completer) Backends

func (c *Completer) Backends() map[string]string

Backends returns the current per-component backend map.

func (*Completer) Complete

func (c *Completer) Complete(input string, contextPath []string) []Completion

Complete returns completions for the given input at cursor position. contextPath is the current edit context (e.g., ["bgp", "peer", "192.168.1.1"]).

func (*Completer) GetListKeyEntry

func (c *Completer) GetListKeyEntry(listPath []string) *gyang.Entry

GetListKeyEntry returns the YANG entry for a list's key leaf. For example, peer list with key "address" returns the address leaf entry.

func (*Completer) GhostText

func (c *Completer) GhostText(input string, contextPath []string) string

GhostText returns the best single completion for inline ghost text.

func (*Completer) SetTree

func (c *Completer) SetTree(tree any)

SetTree sets the config tree for data-aware completion.

func (*Completer) TypeHint

func (c *Completer) TypeHint(t *gyang.YangType) string

TypeHint returns a hint string for a YANG type.

func (*Completer) ValidateValueAtPath

func (c *Completer) ValidateValueAtPath(path []string, value string) error

ValidateValueAtPath validates a value against the YANG type at the given path. Returns nil if valid, an error describing why the value is invalid. Path should include the leaf name (e.g., ["bgp", "peer", "1.1.1.1", "receive-hold-time"]).

type Completion

type Completion = contract.Completion

Completion represents a single completion suggestion. Completion is a type alias of contract.Completion.

type ConfigValidationError

type ConfigValidationError struct {
	Line     int    // 1-based line number (0 if unknown)
	Column   int    // 1-based column (0 if unknown)
	Message  string // Human-readable message
	Severity string // severityError or severityWarning
}

ConfigValidationError represents a single validation error or warning.

type ConfigValidationResult

type ConfigValidationResult struct {
	Errors   []ConfigValidationError
	Warnings []ConfigValidationError
}

ConfigValidationResult contains all validation errors and warnings.

func (*ConfigValidationResult) HasErrors

func (r *ConfigValidationResult) HasErrors() bool

HasErrors returns true if there are any errors.

func (*ConfigValidationResult) HasWarnings

func (r *ConfigValidationResult) HasWarnings() bool

HasWarnings returns true if there are any warnings.

type ConfigValidator

type ConfigValidator struct {
	// contains filtered or unexported fields
}

ConfigValidator provides configuration text validation. Uses YANG-derived schema for parsing and validation.

func NewConfigValidator

func NewConfigValidator() (*ConfigValidator, error)

NewConfigValidator creates a new config validator. Returns error if YANG schema cannot be loaded.

func (*ConfigValidator) Validate

func (v *ConfigValidator) Validate(content string) ConfigValidationResult

Validate runs all validation levels and returns the result. Detects the config format (hierarchical vs set/set-meta) and uses the appropriate parser. This is necessary because WorkingContent() returns set+meta format when a session is active.

func (*ConfigValidator) ValidateSemantic

func (v *ConfigValidator) ValidateSemantic(tree *config.Tree) ([]ConfigValidationError, []ConfigValidationError)

ValidateSemantic validates semantic constraints on parsed tree. Delegates to ValidateWithYANG for YANG-based validation.

func (*ConfigValidator) ValidateSyntax

func (v *ConfigValidator) ValidateSyntax(content string) []ConfigValidationError

ValidateSyntax validates only syntax using YANG-derived schema.

func (*ConfigValidator) ValidateTransition

func (v *ConfigValidator) ValidateTransition(previous, candidate string) ConfigValidationResult

ValidateTransition validates a candidate config with access to the previous config, so plugin verifiers see deleted roots as runtime reload would. Uses the transition-aware path instead of Validate to avoid running plugin verifiers twice for roots present in both configs.

func (*ConfigValidator) ValidateWithYANG

func (v *ConfigValidator) ValidateWithYANG(tree *config.Tree) ([]ConfigValidationError, []ConfigValidationError)

ValidateWithYANG validates the parsed tree using YANG constraints. Uses recursive ValidateTree for systematic validation of all leaves. Group peers inherit group-level fields merged with peer-level fields. Returns (errors, warnings). Mandatory-missing fields are warnings, value errors are errors.

type Conflict

type Conflict = contract.Conflict

Conflict describes a single conflict detected during commit. Conflict is a type alias of contract.Conflict.

type ConflictType

type ConflictType = contract.ConflictType

ConflictType is a type alias of contract.ConflictType.

type DashboardFactory

type DashboardFactory = contract.DashboardFactory

DashboardFactory creates a dashboard polling function. The returned function calls commandExecutor("bgp summary") and returns the JSON. DashboardFactory creates a dashboard poller. Type alias of contract.DashboardFactory so ssh, web, and hub use the same type.

type EditSession

type EditSession struct {
	User      string    // User identifier (e.g., "thomas")
	Origin    string    // Origin: "local" for terminal, "ssh" for SSH sessions
	ID        string    // Full session ID matching MetaEntry.SessionKey(): "user@origin%RFC3339time"
	StartTime time.Time // When the session was created
}

EditSession represents an editing session identity for concurrent config editing. Each editor instance gets a unique session, used to track authorship in the draft file.

func NewEditSession

func NewEditSession(user, origin string) *EditSession

NewEditSession creates a new editing session with the given user and origin. The session ID matches MetaEntry.SessionKey() format: "user@origin%RFC3339time". The user is sanitized via filepath.Base to prevent path traversal. Callers should validate user with ValidateUser at input boundaries.

func (*EditSession) OrphanedSessions

func (s *EditSession) OrphanedSessions(allSessions []string) []string

OrphanedSessions filters a list of session IDs to those belonging to the same user and origin as this session but with a different timestamp (i.e., from a previous session). Uses "user@origin%" prefix matching -- the % delimiter ensures "thomas@local" does not match "thomasmore@local".

func (*EditSession) UserAtOrigin

func (s *EditSession) UserAtOrigin() string

UserAtOrigin returns "user@origin" for metadata prefixes.

type Editor

type Editor struct {
	// contains filtered or unexported fields
}

Editor manages an editing session for a configuration file. The tree is the canonical in-memory representation when treeValid is true. WorkingContent() returns Serialize(tree) when tree is valid, otherwise falls back to stored raw text for configs that can't be parsed.

func NewEditor

func NewEditor(configPath string) (*Editor, error)

NewEditor creates a new editor for the given configuration file. Uses filesystem storage by default. For blob storage, use NewEditorWithStorage.

func NewEditorWithStorage

func NewEditorWithStorage(store storage.Storage, configPath string) (*Editor, error)

NewEditorWithStorage creates a new editor backed by the given storage. All file I/O (config, draft, backup, lock) goes through the storage interface.

func (*Editor) ActivateLeaf

func (e *Editor) ActivateLeaf(parentPath []string, leafName string) error

ActivateLeaf clears the inactive marker on a leaf at parentPath. Returns ErrLeafNotInactive (wrapped) when the leaf is already active.

func (*Editor) ActivateLeafListValue

func (e *Editor) ActivateLeafListValue(path []string, leafListName, value string) error

ActivateLeafListValue removes "inactive:" prefix from a value in a leaf-list.

func (*Editor) ActivatePath

func (e *Editor) ActivatePath(path []string) error

ActivatePath clears the inactive flag on the container or list entry at path. Strict on path resolution.

Returns ErrPathNotFound or ErrPathNotInactive (wrapped) for the idempotent / mistyped-path cases.

func (*Editor) ActiveContentAtPath

func (e *Editor) ActiveContentAtPath(path []string) string

ActiveContentAtPath returns config text showing only active nodes (inactive pruned).

func (*Editor) ActiveSessions

func (e *Editor) ActiveSessions() []string

ActiveSessions returns all session IDs with pending changes. Scans change files to include other users' sessions.

func (*Editor) AdoptSession

func (e *Editor) AdoptSession(oldSessionID string) error

AdoptSession rewrites all entries belonging to oldSessionID to the current session. Used when a user reconnects and wants to take over an orphaned session's changes.

func (*Editor) AnnotatedView

func (e *Editor) AnnotatedView(path []string, columns config.ShowColumns, setFormat bool) string

AnnotatedView returns config content annotated with the enabled metadata columns. When setFormat is true, produces flat set commands; otherwise hierarchical tree. At a sub-path, the metadata tree is walked to match the context path.

func (*Editor) AutoSelectListEntry

func (e *Editor) AutoSelectListEntry(path []string) []string

AutoSelectListEntry checks if the path ends at a list node with exactly one entry. If so, it returns the expanded path with the single entry's key appended. Otherwise returns the original path unchanged.

func (*Editor) BlameView

func (e *Editor) BlameView() string

BlameView returns a blame-annotated view of the configuration. When no metadata exists, uses an empty MetaTree to produce a consistent hierarchical tree format with empty blame gutters.

func (*Editor) CheckDraftChanged

func (e *Editor) CheckDraftChanged() (changed bool, notification string)

CheckDraftChanged checks if the draft file has been modified by another session. Uses Storage.Stat for both filesystem (OS mtime) and blob (tracked mtime). Returns true if the draft mtime is newer than the last known mtime. Also re-reads and re-parses the draft on change to update in-memory state.

func (*Editor) Close

func (e *Editor) Close() error

Close cleans up any resources.

func (*Editor) CommitSession

func (e *Editor) CommitSession() (*CommitResult, error)

CommitSession commits the current session's changes to config.conf. First saves (change file → draft), then applies draft to config.conf. Returns a CommitResult with conflicts (if any) or the number of applied changes.

func (*Editor) CommitSessionCandidate

func (e *Editor) CommitSessionCandidate(stamp time.Time) (*CommitResult, string, error)

CommitSessionCandidate merges the current session's changes into a staged candidate version instead of overwriting the active config file. Session draft/change files are left intact until MarkCommittedContent confirms the daemon promoted the candidate.

func (*Editor) ContentAtPath

func (e *Editor) ContentAtPath(path []string) string

ContentAtPath returns the serialized content at the given context path in tree format. Always returns tree (junos-style) format for display, regardless of session state. If the path doesn't resolve, falls back to full tree content.

func (*Editor) ContentWithoutUser

func (e *Editor) ContentWithoutUser(username string) string

ContentWithoutUser returns the config as it would be without the specified user's changes. Clones the working tree, then reverts each leaf that the user changed to its Previous value. Leaves with no Previous value (new additions by the user) are deleted. Returns serialized tree content, or empty string if no metadata or no changes by that user.

func (*Editor) CopyListEntry

func (e *Editor) CopyListEntry(parentPath []string, listName, srcKey, dstKey string) error

CopyListEntry clones a list entry under a new key at the given path. The parentPath navigates to the tree containing the list. MetaTree is not updated because copy is blocked in session mode (meta is session-only).

func (*Editor) CreateEntry

func (e *Editor) CreateEntry(path []string) error

CreateEntry creates an empty list entry at the given path. The path must end at a list entry (e.g., ["bgp", "peer", "london"]). If the entry already exists, this is a no-op.

func (*Editor) DeactivateLeaf

func (e *Editor) DeactivateLeaf(parentPath []string, leafName string) error

DeactivateLeaf marks a leaf inactive on the tree at parentPath. The leaf value (if any) is preserved verbatim; PruneInactive removes the entry at apply time so consumers see it as absent. Permissive on absent leaves -- pre-marking before set is allowed, matching the Tree.SetLeafInactive contract; this is what lets a leaf with a YANG default be deactivated without a prior explicit set.

Returns ErrLeafAlreadyInactive (wrapped) when the leaf is already marked, so callers can use errors.Is for idempotent flows.

func (*Editor) DeactivateLeafListValue

func (e *Editor) DeactivateLeafListValue(path []string, leafListName, value string) error

DeactivateLeafListValue adds "inactive:" prefix to a value in a leaf-list.

func (*Editor) DeactivatePath

func (e *Editor) DeactivatePath(path []string) error

DeactivatePath sets the inactive flag on the container or list entry at path. Strict on path resolution: it rejects non-existent paths rather than silently materializing them (which is what plain SetValue + walkOrCreate would do).

Returns ErrPathNotFound when the path does not resolve in the tree, and ErrPathAlreadyInactive (wrapped) when the inactive flag is already set, so callers can use errors.Is for idempotent flows.

func (*Editor) DeleteByPath

func (e *Editor) DeleteByPath(fullPath []string) error

DeleteByPath deletes the element at the given absolute path using schema awareness. It determines whether the target is a leaf, container, or list entry and calls the appropriate delete method.

func (*Editor) DeleteContainer

func (e *Editor) DeleteContainer(path []string, name string) error

DeleteContainer removes a container at the given path in the tree.

func (*Editor) DeleteLeafListValue

func (e *Editor) DeleteLeafListValue(path []string, leafListName, value string) error

DeleteLeafListValue removes one member from a leaf-list (the inverse of add-member set). Returns an error if the member is not present.

func (*Editor) DeleteList

func (e *Editor) DeleteList(path []string, name string) error

DeleteList removes an entire list (all entries) at the given path.

func (*Editor) DeleteListEntry

func (e *Editor) DeleteListEntry(path []string, listName, key string) error

DeleteListEntry removes a list entry at the given path in the tree.

func (*Editor) DeleteLive

func (e *Editor) DeleteLive()

DeleteLive removes the .live.conf file if it exists. Errors are ignored because the file may not exist.

func (*Editor) DeleteValue

func (e *Editor) DeleteValue(path []string, key string) error

DeleteValue removes a leaf value at the given path in the tree.

func (*Editor) DetectConflicts

func (e *Editor) DetectConflicts() []Conflict

DetectConflicts scans pending changes from other sessions and reports live overlaps.

func (*Editor) Diff

func (e *Editor) Diff() string

Diff returns a simple diff between original and working content.

func (*Editor) DiffGutterEnabled

func (e *Editor) DiffGutterEnabled() bool

DiffGutterEnabled returns whether the diff gutter (+/-) markers are shown.

func (*Editor) Dirty

func (e *Editor) Dirty() bool

Dirty returns true if there are unsaved changes.

func (*Editor) Discard

func (e *Editor) Discard() error

Discard reverts working content and tree to original state.

func (*Editor) DiscardSessionPath

func (e *Editor) DiscardSessionPath(path []string) error

DiscardSessionPath discards this session's changes at the given path. If path is nil, discards all changes (deletes change file, reloads from base).

func (*Editor) DisconnectSession

func (e *Editor) DisconnectSession(sessionID string) error

DisconnectSession removes another user's change file. In the per-user change file model, each user has their own file, so disconnect simply deletes the other user's change file.

func (*Editor) EnsureListEntry

func (e *Editor) EnsureListEntry(parentPath []string, listName, key string) error

EnsureListEntry creates a list entry if it does not already exist. Validates the key value against the list's YANG key type.

func (*Editor) HasArchiveNotifier

func (e *Editor) HasArchiveNotifier() bool

HasArchiveNotifier returns true if an archive notifier is configured.

func (*Editor) HasDraft

func (e *Editor) HasDraft() bool

HasDraft returns true if a draft file exists for this config.

func (*Editor) HasPendingEdit

func (e *Editor) HasPendingEdit() bool

HasPendingEdit returns true if an edit file exists from a previous session.

func (*Editor) HasPendingLive

func (e *Editor) HasPendingLive() bool

HasPendingLive returns true if a .live.conf file exists. This indicates an unconfirmed "commit confirmed" from a previous session.

func (*Editor) HasPendingSessionChanges

func (e *Editor) HasPendingSessionChanges() bool

HasPendingSessionChanges returns true if this session has pending changes in the draft.

func (*Editor) HasReloadNotifier

func (e *Editor) HasReloadNotifier() bool

HasReloadNotifier returns true if a reload notifier is configured. Use this to distinguish "no daemon" from "reload succeeded".

func (*Editor) HasSession

func (e *Editor) HasSession() bool

HasSession returns true if a concurrent editing session is active.

func (*Editor) InactiveContentAtPath

func (e *Editor) InactiveContentAtPath(path []string) string

InactiveContentAtPath returns config text showing only inactive nodes. Active nodes are pruned; inactive nodes are shown with their full subtree.

func (*Editor) InsertLeafListValue

func (e *Editor) InsertLeafListValue(path []string, leafListName, value, position, ref string) error

InsertLeafListValue inserts a value into a leaf-list at the specified position. path navigates to the container holding the leaf-list. leafListName is the leaf-list field name. position is first/last/before/after, ref is the reference value for before/after.

func (*Editor) IsListKeyLeafPath

func (e *Editor) IsListKeyLeafPath(path []string) bool

IsListKeyLeafPath checks if the last two path elements are a list name and its key leaf keyword. Uses the config schema which flattens choice/case.

func (*Editor) ListBackups

func (e *Editor) ListBackups() ([]BackupInfo, error)

ListBackups returns available backup files, sorted by timestamp descending.

func (*Editor) ListKeys

func (e *Editor) ListKeys(listName string) []string

ListKeys returns the keys for a list at the given path (e.g., "neighbor").

func (*Editor) LivePath

func (e *Editor) LivePath() string

LivePath returns the path to the .live.conf file. This file holds the trial config during a "commit confirmed" window.

func (*Editor) LoadDraft

func (e *Editor) LoadDraft() bool

LoadDraft reads the draft file and loads its content into the editor's in-memory tree. Called on startup to restore previously saved work. Returns false if no draft exists.

func (*Editor) LoadPendingEdit

func (e *Editor) LoadPendingEdit() error

LoadPendingEdit loads the content from the .edit file.

func (*Editor) LookupSchemaNode

func (e *Editor) LookupSchemaNode(path []string) config.Node

LookupSchemaNode returns the schema node at the terminus of path. Unlike WalkPathWithSchema, this walks the schema only (no tree walk), so it resolves leaves as well as containers and list entries. Used by one-shot CLI verbs that need to dispatch deactivate/activate based on the node kind regardless of whether the value is currently set.

List keys interleaved in the path are skipped (a list child consumes two tokens: its name and its key value).

func (*Editor) MarkCommittedContent

func (e *Editor) MarkCommittedContent(content string)

MarkCommittedContent updates editor state after the daemon has promoted a candidate.

func (*Editor) MarkDirty

func (e *Editor) MarkDirty()

MarkDirty marks the editor as having unsaved changes.

func (*Editor) NotifyArchive

func (e *Editor) NotifyArchive(content []byte) []error

NotifyArchive calls the archive notifier if one is configured. Returns nil if no notifier is set or if archival succeeds.

func (*Editor) NotifyReload

func (e *Editor) NotifyReload() error

NotifyReload calls the reload notifier if one is configured. Returns nil if no notifier is set or if notification succeeds.

func (*Editor) OriginalContent

func (e *Editor) OriginalContent() string

OriginalContent returns the original file content.

func (*Editor) OriginalContentAtPath

func (e *Editor) OriginalContentAtPath(path []string) string

OriginalContentAtPath returns the serialized content from the original (on-disk) config at the given context path in tree format. Re-parses originalContent on each call (config files are small, and this is only called on user interaction, not hot path). Always returns tree format to match ContentAtPath, regardless of the stored format. Returns empty string if path doesn't resolve in the original config.

func (*Editor) OriginalPath

func (e *Editor) OriginalPath() string

OriginalPath returns the path to the original configuration file.

func (*Editor) PendingChanges

func (e *Editor) PendingChanges(sessionID string) []config.PendingChange

PendingChanges returns the unified pending-change view for a specific session, or all sessions when sessionID is empty.

func (*Editor) PendingEditDiff

func (e *Editor) PendingEditDiff() string

PendingEditDiff returns the diff between original and pending edit content. Returns empty string if no edit file exists.

func (*Editor) PendingEditTime

func (e *Editor) PendingEditTime() time.Time

PendingEditTime returns the modification time of the .edit file. Returns zero time if no edit file exists. For blob storage, mod time is unavailable so this returns zero time even when the edit exists; callers handle zero time gracefully in the prompt.

func (*Editor) PromptPendingEdit

func (e *Editor) PromptPendingEdit() PendingEditAction

PromptPendingEdit prompts user about existing uncommitted changes. Reads from stdin, writes to stdout.

func (*Editor) ReadBackupContent

func (e *Editor) ReadBackupContent(path string) ([]byte, error)

ReadBackupContent reads the content of a backup by its path.

func (*Editor) RenameListEntry

func (e *Editor) RenameListEntry(parentPath []string, listName, oldKey, newKey string) error

RenameListEntry renames a list entry key at the given path. The parentPath navigates to the tree containing the list. In session mode, the rename is recorded as a structural op in the per-user change file and the in-memory tree/meta are updated immediately.

func (*Editor) ResolveLeafListValue

func (e *Editor) ResolveLeafListValue(fullPath []string) (parentPath []string, leafListName string, ok bool)

ResolveLeafListValue checks whether fullPath terminates inside a leaf-list value (e.g. `bgp filter import no-self-as`). Returns the tree-level parent path (to the container holding the leaf-list), the leaf-list field name, and true if so. Handles list keys interleaved in the path (e.g. `bgp peer peer1 filter import value`).

The Model's TUI dispatch and the one-shot CLI verbs both call this to route deactivate/activate to DeactivateLeafListValue.

func (*Editor) RestoreOriginalContent

func (e *Editor) RestoreOriginalContent(content string) error

RestoreOriginalContent writes the previous committed content back to disk after a failed runtime reload while keeping the current candidate in memory.

func (*Editor) Rollback

func (e *Editor) Rollback(backupPath string) error

Rollback restores the configuration from a backup file. Creates a backup of the current config first, so the rollback itself can be undone.

func (*Editor) Save

func (e *Editor) Save() error

Save commits changes: creates backup of original, writes serialized tree. Returns an error when a session is active -- use CommitSession() instead.

func (*Editor) SaveDraft

func (e *Editor) SaveDraft() error

SaveDraft applies changes from the per-user change file to config.conf.draft. Creates a new draft (base + own changes), then deletes the change file.

func (*Editor) SaveEditState

func (e *Editor) SaveEditState() error

SaveEditState saves the current working content to the .edit file.

func (*Editor) SaveLive

func (e *Editor) SaveLive() error

SaveLive writes the current working content to the .live.conf file. Used by "commit confirmed" to create the trial config.

func (*Editor) SavedDraftContent

func (e *Editor) SavedDraftContent() string

SavedDraftContent returns the content of the saved draft file on disk. Returns empty string if no draft exists.

func (*Editor) SensitiveKeys

func (e *Editor) SensitiveKeys() map[string]bool

SensitiveKeys returns the set of leaf names marked ze:sensitive in the schema.

func (*Editor) SessionChanges

func (e *Editor) SessionChanges(sessionID string) []config.SessionEntry

SessionChanges returns the changes for a specific session, or all sessions. If sessionID is empty, returns changes for all sessions (scanning change files).

func (*Editor) SessionID

func (e *Editor) SessionID() string

SessionID returns the current session's ID, or empty string if no session.

func (*Editor) SetArchiveNotifier

func (e *Editor) SetArchiveNotifier(fn archive.Notifier)

SetArchiveNotifier sets an optional function to archive config after save. When set, commit will call this after writing config to disk. When nil (no archive locations configured), no archival is attempted.

func (*Editor) SetDiffGutter

func (e *Editor) SetDiffGutter(enabled bool)

SetDiffGutter enables or disables the diff gutter (+/-) markers.

func (*Editor) SetPreCommitValidate

func (e *Editor) SetPreCommitValidate(fn func(candidate string) error)

SetPreCommitValidate sets a function called during SaveDraft to validate the candidate config before writing. If the function returns an error, the save is rejected and the draft is not written.

func (*Editor) SetReloadNotifier

func (e *Editor) SetReloadNotifier(fn ReloadNotifier)

SetReloadNotifier sets an optional function to notify the daemon after save. When set, commit will call this after writing config to disk. When nil (standalone mode), no notification is attempted.

func (*Editor) SetSession

func (e *Editor) SetSession(session *EditSession)

SetSession sets the concurrent editing session identity. When set, SetValue and DeleteValue use write-through to the draft file.

func (*Editor) SetShowColumn

func (e *Editor) SetShowColumn(column string, enabled bool)

SetShowColumn enables or disables a show column preference. Only valid column names (author, date, source, changes) are accepted.

func (*Editor) SetValue

func (e *Editor) SetValue(path []string, key, value string) error

SetValue sets a leaf value at the given path in the tree. Plain leaf-lists (ValueOrArrayNode) get JunOS add-member semantics: each set appends one member (idempotently); it never replaces the list. The member lands in the multi-value store — the store every serializer reads — not the scalar map, where it would be silently dropped.

func (*Editor) SetView

func (e *Editor) SetView() string

SetView returns the flat set-format view of the configuration. Always emits bare set commands without metadata (AC-15: exportable format).

func (*Editor) SetWorkingContent

func (e *Editor) SetWorkingContent(content string)

SetWorkingContent sets the working content and parses it into the tree. If parsing fails, falls back to raw text mode (treeValid = false).

func (*Editor) ShowColumnEnabled

func (e *Editor) ShowColumnEnabled(column string) bool

ShowColumnEnabled returns whether a show column is enabled.

func (*Editor) StageCandidate

func (e *Editor) StageCandidate(stamp time.Time) (string, string, error)

StageCandidate writes the current working config as a timestamped candidate. It does not update the editor's committed state; callers do that only after the daemon promotes the candidate.

func (*Editor) Tree

func (e *Editor) Tree() *config.Tree

Tree returns the parsed configuration tree.

func (*Editor) WalkPath

func (e *Editor) WalkPath(path []string) *config.Tree

WalkPath navigates the tree using the schema to distinguish containers from list keys. Returns the subtree at the given path, or nil if the path doesn't resolve.

func (*Editor) WalkPathWithSchema

func (e *Editor) WalkPathWithSchema(path []string) (*config.Tree, config.Node)

WalkPathWithSchema is the exported form of walkPathWithSchema. Returns the (sub-tree, schema node) pair at the given path. Halts at structural nodes (containers, lists) -- for terminal-leaf lookups use LookupSchemaNode instead.

func (*Editor) WorkingContent

func (e *Editor) WorkingContent() string

WorkingContent returns the current working content. When a session is active with metadata, returns set+meta format (matching what CommitSession writes). Otherwise returns hierarchical format. Falls back to raw text if tree is not valid.

type EditorMode

type EditorMode int

EditorMode represents the current editor mode.

const (
	// ModeConfig is the config editing mode (default when editor loaded).
	ModeConfig EditorMode = iota
	// ModeOperational is the operational command mode.
	ModeOperational
)

func (EditorMode) String

func (m EditorMode) String() string

String returns the mode name.

type History

type History struct {
	// contains filtered or unexported fields
}

History manages command history entries with browsing and optional persistence. It owns the entries list, the Up/Down browsing state, and the save/load logic. A nil rw means no persistence (in-memory only, graceful degradation).

func NewHistory

func NewHistory(rw historyRW, username string) *History

NewHistory creates a History backed by the given reader/writer. Pass nil for no persistence (in-memory only). The username scopes history storage per user (meta/history/<user>/<mode>). The max entry count is read from meta/history/max (default 100).

func (*History) Append

func (h *History) Append(cmd string) bool

Append adds a command to history with consecutive dedup. Empty commands and commands containing newlines are rejected. Returns true if the entry was added (not a duplicate or invalid).

func (*History) Down

func (h *History) Down() (string, bool)

Down recalls the next command from history, or restores the saved input. Returns the value and true if there is something to show, or empty and false if not currently browsing.

func (*History) Entries

func (h *History) Entries() []string

Entries returns the current history entries.

func (*History) Load

func (h *History) Load(mode string) []string

Load reads the history for the given mode from the store. Returns nil if no history exists or the store is nil. Results are trimmed to the configured max.

func (*History) Max

func (h *History) Max() int

Max returns the configured maximum history entries.

func (*History) ResetBrowsing

func (h *History) ResetBrowsing()

ResetBrowsing clears the browsing state without changing entries. Called when the user types a character, so the next Up starts fresh.

func (*History) Save

func (h *History) Save(mode string)

Save writes the history for the given mode to the store. Trims to the configured max, keeping the newest entries. No-op if the store is nil.

func (*History) Up

func (h *History) Up(currentInput string) (string, bool)

Up recalls the previous command from history. On first call, saves currentInput and returns the most recent entry. Returns the recalled value and true, or empty string and false if history is empty.

type LoginWarning

type LoginWarning = contract.LoginWarning

LoginWarning is a single warning shown at CLI login. Type alias of contract.LoginWarning so ssh, web, and hub use the same type.

type Model

type Model struct {
	// contains filtered or unexported fields
}

Model is the Bubble Tea model for the editor.

func NewCommandModel

func NewCommandModel() Model

NewCommandModel creates a command-only model with no editor. Used by ze cli and plugin CLI where no config file is loaded. The model starts in ModeOperational with config commands unavailable.

func NewModel

func NewModel(ed *Editor) (Model, error)

NewModel creates a new editor model.

func (*Model) ApplyResult

func (m *Model) ApplyResult(r commandResult)

ApplyResult applies a commandResult to the model. Useful for testing to simulate what the Update handler does.

func (Model) Completions

func (m Model) Completions() []Completion

Completions returns the current completion list.

func (Model) ConfirmTimerActive

func (m Model) ConfirmTimerActive() bool

ConfirmTimerActive returns true if a commit confirm timer is active.

func (Model) ContextPath

func (m Model) ContextPath() []string

ContextPath returns the current context path.

func (Model) Dirty

func (m Model) Dirty() bool

Dirty returns true if there are unsaved changes.

func (Model) Error

func (m Model) Error() error

Error returns the current command error.

func (Model) GhostText

func (m Model) GhostText() string

GhostText returns the current ghost text suggestion.

func (Model) Init

func (m Model) Init() tea.Cmd

Init implements tea.Model.

func (Model) InputValue

func (m Model) InputValue() string

InputValue returns the current text input value.

func (Model) IsDashboard

func (m Model) IsDashboard() bool

IsDashboard returns true if the dashboard is active.

func (Model) IsMonitoring

func (m Model) IsMonitoring() bool

IsMonitoring returns true if a monitor session is active.

func (Model) IsPingMonitor

func (m Model) IsPingMonitor() bool

IsPingMonitor returns true if the ping monitor is active.

func (Model) IsPingMonitorPiped

func (m Model) IsPingMonitorPiped() bool

IsPingMonitorPiped returns true if a piped ping session is active.

func (Model) IsTemplate

func (m Model) IsTemplate() bool

IsTemplate returns true if in template editing mode.

func (Model) IsTraceroute

func (m Model) IsTraceroute() bool

IsTraceroute returns true if the traceroute monitor is active.

func (Model) IsTraceroutePiped

func (m Model) IsTraceroutePiped() bool

IsTraceroutePiped returns true if a piped traceroute session is active.

func (Model) Mode

func (m Model) Mode() EditorMode

Mode returns the current editor mode.

func (Model) SelectedIndex

func (m Model) SelectedIndex() int

SelectedIndex returns the currently selected dropdown index.

func (*Model) SetAuditRecorder

func (m *Model) SetAuditRecorder(recorder audit.Recorder, surface, username, remoteAddr string)

SetAuditRecorder sets the audit sink and caller metadata for config mutations.

func (*Model) SetCommandCompleter

func (m *Model) SetCommandCompleter(cc CommandModeCompleter)

SetCommandCompleter sets the command mode completer. When set, command mode provides operational command completions. When nil, command mode has no completions (editor-only / standalone mode). Accepts any CommandModeCompleter (e.g., *CommandCompleter or *PluginCompleter).

func (*Model) SetCommandExecutor

func (m *Model) SetCommandExecutor(fn func(string) (string, error))

SetCommandExecutor sets the function used to execute operational commands in command mode. The function receives a command string and returns the output or an error. When nil, command mode shows an error on Enter.

func (*Model) SetDashboardFactory

func (m *Model) SetDashboardFactory(f DashboardFactory)

SetDashboardFactory sets the factory used to create dashboard sessions.

func (*Model) SetHistory

func (m *Model) SetHistory(h *History)

SetHistory replaces the model's history with a persistent History. Loads saved entries for the current mode and pre-loads the other mode into modeStates so history is available on mode switch.

func (*Model) SetInput

func (m *Model) SetInput(value string)

SetInput sets the text input value. Used by external packages (e.g. SSH) that cannot access the unexported textInput field directly.

func (*Model) SetLoginWarnings

func (m *Model) SetLoginWarnings(warnings []LoginWarning)

SetLoginWarnings sets the login warnings to display in the welcome area. Called by the SSH session after collecting warnings from the daemon.

func (*Model) SetMonitorFactory

func (m *Model) SetMonitorFactory(f MonitorFactory)

SetMonitorFactory sets the factory used to create monitor sessions. When set, streaming commands (e.g., "monitor event") in the interactive TUI enter streaming mode. When nil, streaming commands fall through to the regular executor (non-streaming).

func (*Model) SetPingFactory

func (m *Model) SetPingFactory(f PingFactory)

SetPingFactory sets the factory used to run continuous ping sessions.

func (*Model) SetRestartFunc

func (m *Model) SetRestartFunc(fn func())

SetRestartFunc sets the callback for the "restart" interactive CLI command. When set, typing "restart" prompts for confirmation, then calls fn and quits.

func (*Model) SetShutdownFunc

func (m *Model) SetShutdownFunc(fn func())

SetShutdownFunc sets the callback for the "stop" interactive CLI command. When set, typing "stop" prompts for confirmation, then calls fn and quits.

func (*Model) SetTracerouteFactory

func (m *Model) SetTracerouteFactory(f TracerouteFactory)

SetTracerouteFactory sets the factory used to run traceroute probes.

func (Model) ShowDropdown

func (m Model) ShowDropdown() bool

ShowDropdown returns true if the completion dropdown is visible.

func (Model) StatusMessage

func (m Model) StatusMessage() string

StatusMessage returns the current status message.

func (*Model) SwitchMode

func (m *Model) SwitchMode(target EditorMode)

SwitchMode switches the editor to the given mode, saving and restoring screen state.

func (Model) Update

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model.

func (*Model) UpdateCompletions

func (m *Model) UpdateCompletions()

UpdateCompletions refreshes the completion list based on current input. Useful for testing to ensure completions are populated.

func (Model) ValidationErrors

func (m Model) ValidationErrors() []ConfigValidationError

ValidationErrors returns the current validation errors.

func (Model) ValidationWarnings

func (m Model) ValidationWarnings() []ConfigValidationError

ValidationWarnings returns the current validation warnings.

func (Model) View

func (m Model) View() tea.View

View implements tea.Model.

func (Model) ViewportContent

func (m Model) ViewportContent() string

ViewportContent returns the content currently displayed in the viewport.

type MonitorFactory

type MonitorFactory = contract.MonitorFactory

MonitorFactory creates a monitor session for the given arguments. The context controls the session lifetime; cancel stops delivery. args are the parsed monitor keywords (e.g., ["peer", "10.0.0.1"]). MonitorFactory creates a monitor session. Type alias of contract.MonitorFactory so ssh, web, and hub use the same type.

type MonitorSession

type MonitorSession = contract.MonitorSession

MonitorSession represents an active monitor streaming session inside the TUI. The model holds at most one active session. Events arrive on EventChan; the model polls with a ticker and appends them to the viewport. MonitorSession represents an active monitor streaming session inside the TUI. Type alias of contract.MonitorSession so ssh, web, and hub use the same type.

type PendingEditAction

type PendingEditAction int

PendingEditAction represents user's choice for pending edit file.

const (
	// PendingEditContinue - continue editing from pending file.
	PendingEditContinue PendingEditAction = iota
	// PendingEditDiscard - discard pending file, start fresh.
	PendingEditDiscard
	// PendingEditQuit - quit without editing.
	PendingEditQuit
)

type PingFactory

type PingFactory func(ctx context.Context, target string, interval, timeout time.Duration) (<-chan map[string]any, context.CancelFunc, error)

PingFactory starts a continuous ping session. The returned channel receives per-reply results until the context is canceled.

type PipeFilter

type PipeFilter struct {
	Type string // "grep", "head", "tail", "format", "compare"
	Arg  string // Pattern or count
}

PipeFilter represents a filter in a pipe chain.

func ParsePipeFilters

func ParsePipeFilters(tokens []string) []PipeFilter

ParsePipeFilters parses pipe filter tokens into PipeFilter structs.

type PluginCompleter

type PluginCompleter struct {
	// contains filtered or unexported fields
}

PluginCompleter provides tab completion for plugin SDK methods. Used by `ze bgp plugin cli` interactive mode after 5-stage negotiation.

func NewPluginCompleter

func NewPluginCompleter() *PluginCompleter

NewPluginCompleter creates a completer for plugin SDK methods.

func (*PluginCompleter) Complete

func (c *PluginCompleter) Complete(input string) []Completion

Complete returns completions for the given input.

func (*PluginCompleter) GhostText

func (c *PluginCompleter) GhostText(input string) string

GhostText returns the best single completion for inline display.

type ReloadNotifier

type ReloadNotifier func() error

ReloadNotifier is called after a successful save to notify the running daemon. Returns nil on success, or an error if the daemon could not be reached.

func NewSocketReloadNotifier

func NewSocketReloadNotifier(socketPath string) ReloadNotifier

NewSocketReloadNotifier creates a ReloadNotifier that triggers config reload via the daemon's API socket. It sends the "ze-system:daemon-reload" RPC using NUL-framed JSON, the same protocol the CLI uses.

If the socket does not exist or the daemon is not running, the returned function returns an error (which cmdCommit handles gracefully).

type ShowPipeOpts

type ShowPipeOpts struct {
	Format        string       // "tree" (default) or "config"
	CompareTarget string       // "", "confirmed", "saved", "rollback N"
	TreeFilter    string       // "", "active", "inactive"
	Blame         bool         // show | blame
	Changes       bool         // show | changes
	ChangesAll    bool         // show | changes all
	Errors        bool         // show | errors
	History       bool         // show | history
	TextFilters   []PipeFilter // remaining text filters (match, head, tail)
}

ShowPipeOpts holds the classified result of show-specific pipe filters.

func ClassifyShowPipes

func ClassifyShowPipes(filters []PipeFilter) (ShowPipeOpts, error)

ClassifyShowPipes separates show-specific pipes (format, compare, active/inactive) from text filters (match, head, tail). Both the SSH and web CLI call this so the classification logic lives in one place.

type TracerouteFactory

type TracerouteFactory func(ctx context.Context, target string, maxHops int) (<-chan map[string]any, context.CancelFunc, error)

TracerouteFactory starts a streaming probe round. The returned channel receives individual hop results as ICMP responses arrive. It is closed when the round completes (after ~1s). Cancel stops the round early.

type TranscriptWriter

type TranscriptWriter struct {
	// contains filtered or unexported fields
}

TranscriptWriter records CLI commands and their output to a local file. A nil *TranscriptWriter is a valid no-op receiver.

func NewTranscriptWriter

func NewTranscriptWriter(f *os.File, username, remoteHost string) *TranscriptWriter

NewTranscriptWriter creates a TranscriptWriter that writes to the given file. Writes a header with session metadata. The caller is responsible for creating the file and directory. Returns nil if f is nil.

func (*TranscriptWriter) Close

func (w *TranscriptWriter) Close() error

Close closes the transcript file.

func (*TranscriptWriter) Record

func (w *TranscriptWriter) Record(command, output string)

Record appends a command and its output to the transcript file. Errors are silently ignored (best-effort).

Directories

Path Synopsis
Design: docs/architecture/core-design.md — interactive CLI Related: ../../../../cmd/ze/internal/cmdutil/cmdutil.go — shared command utilities (uses BuildCommandTree)
Design: docs/architecture/core-design.md — interactive CLI Related: ../../../../cmd/ze/internal/cmdutil/cmdutil.go — shared command utilities (uses BuildCommandTree)
Design: docs/architecture/core-design.md -- component boundaries (cli/contract)
Design: docs/architecture/core-design.md -- component boundaries (cli/contract)
Design: docs/architecture/config/yang-config-design.md — editor test infrastructure
Design: docs/architecture/config/yang-config-design.md — editor test infrastructure

Jump to

Keyboard shortcuts

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