import type { SSHAuthType, TerminalConfig } from "./index.js"; import type { HostAuthOverrides } from "./auth-protocols.js"; export type Host = { id: string; name: string; username: string; ip: string; port: number; folder: string; /** Sub-host nesting: the id of the host this one is organized under, if any. */ parentHostId?: string | null; /** * Sub-hosts nested under this host, populated client-side by buildHostTree. * A host with children still renders and behaves as a normal, connectable * HostItem row -- this only adds an expand/collapse chevron for its nested * children, it never wraps the host in a synthetic folder node. */ childHosts?: Host[]; online: boolean; status?: "online" | "reachable" | "offline" | "unknown"; cpu: number | null; ram: number | null; lastAccess: string; tags?: string[]; authType: SSHAuthType; shareSshAuth?: boolean; credentialId?: string; overrideCredentialUsername?: boolean; password?: string; hasPassword?: boolean; sudoPassword?: string; hasSudoPassword?: boolean; hasKey?: boolean; hasKeyPassword?: boolean; key?: string; keyPassword?: string; keyType?: string; notes?: string; pin?: boolean; /** Quick connect only: core can save this host as-is. */ quickConnectSavable?: boolean; sortOrder?: number | null; /** Stable identity across a desktop/server sync pair. */ syncId?: string | null; terminalConfig?: Partial; useSocks5?: boolean; socks5Host?: string; socks5Port?: number; connectionOrigin?: "local" | "remote" | null; socks5Username?: string; socks5Password?: string; socks5ProxyChain?: { host: string; port: number; type: 4 | 5 | "http" | "socks4" | "socks5"; username?: string; password?: string; }[]; /** hostid is a legacy lowercase spelling still present in stored rows. */ jumpHosts?: { hostId: string; hostid?: string }[]; portKnockSequence?: { port: number; protocol: "tcp" | "udp"; delay: number; }[]; statusCheckEnabled?: boolean; /** Seconds between status checks; null follows the global setting. */ statusCheckInterval?: number | null; quickActions: { name: string; snippetId: string }[]; enableSsh: boolean; sshPort: number; rdpAuthType?: "direct" | "credential" | "none"; rdpCredentialId?: string; rdpUser?: string; rdpPassword?: string; hasRdpPassword?: boolean; domain?: string; vncAuthType?: "direct" | "credential"; vncCredentialId?: string; vncPassword?: string; hasVncPassword?: boolean; vncUser?: string; telnetAuthType?: "direct" | "credential"; telnetCredentialId?: string; telnetUser?: string; telnetPassword?: string; hasTelnetPassword?: boolean; /** Host-scope plugin settings, keyed by plugin id. Secrets are redacted. */ pluginSettings?: Record>; forceKeyboardInteractive?: boolean; isShared?: boolean; authOverrides?: HostAuthOverrides; permissionLevel?: SharePermissionLevel; sharedExpiresAt?: string; ownerUsername?: string; }; export type SharePermissionLevel = "connect" | "view" | "edit" | "manage"; export type Credential = { id: string; name: string; username: string; type: "password" | "key"; value?: string; password?: string; publicKey?: string; passphrase?: string; description?: string; folder?: string; tags?: string[]; pin?: boolean; sortOrder?: number | null; certPublicKey?: string; /** Set when someone else owns this credential and shared it with you. */ isShared?: boolean; ownerUsername?: string | null; permissionLevel?: "use" | "manage"; }; export type HostFolder = { name: string; children: (Host | HostFolder)[]; path?: string; color?: string; icon?: string; credentialId?: number | null; sortOrder?: number | null; }; /** Core's own tab types. Plugins register theirs at runtime. */ type KnownTabType = | "dashboard" | "host-manager" | "user-profile" | "admin-settings" // Rail panels that can also open full-width in the main area. | "macros" | "ssh-tools" | "split-screen"; /** * TabType covers every built-in tab plus any plugin-contributed tab id. * `string & {}` (rather than plain `string`) keeps IDE autocomplete * suggesting the known members while still accepting an arbitrary id, since * a bare `string` would widen every literal and kill autocomplete entirely. * Plugin tab ids are resolved at render time via the tab-component registry * in tabUtils.tsx, not through this type. */ export type TabType = KnownTabType | (string & {}); export type Tab = { id: string; instanceId: string; type: TabType; label: string; customLabel?: string; host?: Host; openedAt: number; restoredSessionId?: string | null; /** Set when this tab joins someone else's live shared session instead of connecting/attaching its own. */ joinSharedSessionId?: string | null; joinShareId?: string | null; initialFilePath?: string; /** Directory to open a Files tab into, distinct from initialFilePath (a specific file to open in an editor window). */ initialPath?: string; /** Payload owned by the tab's plugin, e.g. which fleet or endpoint it shows. */ data?: Record; /** Present only on a split-screen container tab. Pane ids reference live child tabs. */ splitConfig?: SplitTabConfig; /** Hides this session from the top-level tab bar while it belongs to a split tab. */ parentSplitTabId?: string; terminalRef?: import("react").RefObject<{ disconnect?: () => void; isConnected?: () => boolean; sendInput?: (data: string) => void; subscribeOutput?: (listener: (data: string) => void) => () => void; paste?: (text: string) => void; reconnect?: () => void; fit?: () => void; notifyResize?: () => void; refresh?: () => void; getApplicationCursorKeysMode?: () => boolean; openFileManager?: () => void; focus?: () => void; } | null>; }; /** Core cards are named here; plugin cards add their own ids. */ export type DashboardCardId = | "stats_bar" | "counters_bar" | "quick_actions" | "host_status" | "recent_activity" | (string & {}); export type DashboardCardConfig = { id: DashboardCardId; label: string; description: string; defaultEnabled: boolean; }; export type AdminSection = | "general" | "users" | "sessions" | "roles" | "host-defaults" | "branding" | "database" | "api-keys" | "audit-log" | "ssl"; export type ThemeId = | "dark" | "light" | "system" | "dracula" | "catppuccin" | "nord" | "solarized" | "tokyo-night" | "one-dark" | "gruvbox"; export type FontSizeId = "xs" | "sm" | "md" | "lg" | "xl"; export type UiFontId = | "jetbrains-mono" | "system-sans" | "fira-code" | "source-code-pro" | "caskaydia-cove"; /** A tools panel view: core's own, or a rail panel a plugin registered. */ export type ToolsTab = "ssh-tools" | "macros" | "split-screen" | (string & {}); export type SplitMode = | "none" | "2-way" | "2-way-horizontal" | "3-way" | "3-way-horizontal" | "4-way" | "5-way" | "6-way"; export type SplitTabConfig = { mode: Exclude; paneTabIds: (string | null)[]; rowSizes: number[]; rowColSizes: number[][]; }; export type WorkspaceTabSnapshot = { /** Stable key within the saved tab list, not the live Tab.id (which is regenerated on every open). */ slotId: string; type: TabType; /** Set for host-bound tab types, resolved by Host.syncId on apply. */ hostSyncId?: string | null; /** Denormalized snapshot for display and graceful-skip messaging if the host is later deleted. */ hostNameSnapshot?: string | null; label: string; customLabel?: string; initialFilePath?: string; initialPath?: string; /** Read from payloads saved before tabs carried `data`. */ fleetId?: number; /** The tab's plugin payload. */ data?: Record; }; /** One dock's arrangement. `view` is a RailView, or null when the dock is closed. */ type WorkspaceDockState = { view: string | null; open: boolean; width: number; }; export type WorkspacePayload = { version: 1; tabs: WorkspaceTabSnapshot[]; activeSlotId: string | null; splitMode: SplitMode; /** Indexed identically to AppShell's paneTabIds (length 6), holding slotId instead of a live Tab.id. */ paneTabIds: (string | null)[]; rowSizes: number[]; rowColSizes: number[][]; /** Sidebar arrangement, so a workspace restores the whole layout and not just tabs. */ sidebar?: { left: WorkspaceDockState; right: WorkspaceDockState; }; }; export type Snippet = { id: number; name: string; description?: string; content: string; folder: string | null; order: number; hostIds?: number[]; isNote?: boolean; }; const FOLDER_ICONS = [ "folder", "server", "cloud", "database", "box", "network", "copy", "settings", "cpu", "globe", ] as const; type FolderIconId = (typeof FOLDER_ICONS)[number];