Merge dead-code cleanup: remove 198 unreachable Go funcs + 103 unused frontend exports

Verified via deadcode/ts-prune detection + 317-agent cross-repo verification swarm.
Gated: go build + go vet + test-compile (pulse module), tsc --noEmit (full project
incl tests), npm run lint. Net -2549 lines across 161 files.
This commit is contained in:
rcourtman
2026-06-03 12:57:57 +01:00
161 changed files with 16 additions and 2565 deletions
-18
View File
@@ -1376,24 +1376,6 @@ func applyRemoteSettings(cfg *Config, settings map[string]interface{}, logger *z
}
}
func remoteBoolSetting(settings map[string]interface{}, key string) (bool, bool) {
value, ok := settings[key]
if !ok {
return false, false
}
parsed, ok := value.(bool)
return parsed, ok
}
func remoteStringSetting(settings map[string]interface{}, key string) (string, bool) {
value, ok := settings[key]
if !ok {
return "", false
}
parsed, ok := value.(string)
return parsed, ok
}
func remoteDurationSetting(settings map[string]interface{}, key string) (time.Duration, bool) {
value, ok := settings[key]
if !ok {
-14
View File
@@ -556,20 +556,6 @@ export interface StepResult {
run_at: string;
}
export interface RemediationExecution {
id: string;
plan_id: string;
status: 'pending' | 'approved' | 'running' | 'completed' | 'failed' | 'rolled_back';
approved_by?: string;
approved_at?: string;
started_at?: string;
completed_at?: string;
current_step: number;
step_results?: StepResult[];
error?: string;
rollback_error?: string;
}
// Compatibility response shape for older execution endpoints.
export interface RemediationExecutionResult {
execution_id: string;
-23
View File
@@ -16,29 +16,6 @@ import {
parseOptionalAPIResponseOrNull,
} from './responseUtils';
export interface RemovedDockerHost {
id: string;
hostname?: string;
displayName?: string;
removedAt: number;
}
export interface RemovedHostAgent {
id: string;
hostname?: string;
displayName?: string;
linkedVmId?: string;
linkedContainerId?: string;
removedAt: number;
}
export interface RemovedKubernetesCluster {
id: string;
name?: string;
displayName?: string;
removedAt: number;
}
async function deleteResource<T extends object>(
url: string,
parseErrorMessage: string,
-8
View File
@@ -201,14 +201,6 @@ export function apiErrorCode(error: unknown): string | null {
return trimmedOptionalString((error as APIErrorLike).code);
}
export function apiErrorDetail(error: unknown): string | null {
if (!error || typeof error !== 'object') {
return null;
}
return trimmedOptionalString((error as APIErrorLike).detail);
}
export function apiErrorDetails(error: unknown): Record<string, string> | null {
if (!error || typeof error !== 'object') {
return null;
@@ -359,5 +359,3 @@ export const formatInteger = (value?: number): string => {
if (value === undefined || value === null || Number.isNaN(value)) return '—';
return new Intl.NumberFormat().format(Math.round(value));
};
export const ALIAS_COLLAPSE_THRESHOLD = 4;
@@ -490,7 +490,3 @@ export const useResourceDetailDrawerDerivedState = (
tabs,
};
};
export type ResourceDetailDrawerDerivedState = ReturnType<
typeof useResourceDetailDrawerDerivedState
>;
@@ -95,7 +95,3 @@ export const useResourceDetailDrawerDockerActionsState = (
queueDockerUpdateAll,
};
};
export type UseResourceDetailDrawerDockerActionsStateResult = ReturnType<
typeof useResourceDetailDrawerDockerActionsState
>;
@@ -234,7 +234,3 @@ export const useResourceDetailDrawerHistoryState = (
refetchHistoryFacets,
};
};
export type ResourceDetailDrawerHistoryState = ReturnType<
typeof useResourceDetailDrawerHistoryState
>;
@@ -45,7 +45,3 @@ export function useUnifiedResourceTableViewportSync(
setHostBodyRef,
};
}
export type UnifiedResourceTableViewportSync = ReturnType<
typeof useUnifiedResourceTableViewportSync
>;
@@ -216,16 +216,3 @@ export function isModelProviderConfigured(
const provider = getProviderFromModelId(modelId);
return isAIProviderConfigured(provider, settings);
}
export function groupModelsByProvider(models: AIAvailableModel[]): Map<string, AIAvailableModel[]> {
const grouped = new Map<string, AIAvailableModel[]>();
for (const model of models) {
const provider = getProviderFromModelId(model.id);
const existing = grouped.get(provider) || [];
existing.push(model);
grouped.set(provider, existing);
}
return grouped;
}
@@ -48,34 +48,6 @@ export const matchConfiguredNodeToResource = (
});
};
export const collectConfiguredInfrastructureHosts = (nodes: NodeConfigWithStatus[]) => {
const configuredHosts = new Set<string>();
const clusterMemberIPs = new Set<string>();
nodes.forEach((node) => {
const cleanedHost = node.host.replace(/^https?:\/\//, '').replace(/:\d+$/, '');
configuredHosts.add(cleanedHost.toLowerCase());
if (!('isCluster' in node) || !node.isCluster || !('clusterEndpoints' in node)) {
return;
}
node.clusterEndpoints?.forEach((endpoint: ClusterEndpoint) => {
if (endpoint.ip) {
clusterMemberIPs.add(endpoint.ip.toLowerCase());
}
if (endpoint.host) {
clusterMemberIPs.add(endpoint.host.toLowerCase());
}
});
});
return {
clusterMemberIPs,
configuredHosts,
};
};
const createRepresentedDiscoveryHosts = (): RepresentedDiscoveryHosts => ({
pve: new Set<string>(),
pbs: new Set<string>(),
@@ -12,8 +12,6 @@ import type { DiscoverySettingsFormProps } from './discoverySettingsModel';
import type { TrueNASSettingsPanelState } from './useTrueNASSettingsPanelState';
import type { VMwareSettingsPanelState } from './useVMwareSettingsPanelState';
export type DiscoveryMode = 'auto' | 'custom';
export interface InfrastructurePlatformSettingsProps {
selectedAgent: Accessor<NodeType>;
onSelectAgent: (agent: NodeType) => void;
@@ -1,2 +1 @@
export type { ReportingResourceType } from '@/utils/reportingResourceTypes';
export { toReportingResourceType } from '@/utils/reportingResourceTypes';
@@ -1,16 +1,3 @@
export {
DEFAULT_SETTINGS_TAB,
agentKeyFromPlatformType,
deriveTabFromPath,
deriveTabFromQuery,
isRetiredSettingsCompatibilityPath,
isRouteableSettingsPath,
resolveCanonicalSettingsPath,
settingsAgentLabel,
settingsAgentNodeLabel,
settingsAgentPlatformType,
settingsTabPath,
type AgentKey,
type ProxmoxPlatformType,
type SettingsTab,
} from './settingsNavigationModel';
@@ -1,8 +1,3 @@
export type {
SettingsHeaderMeta,
SettingsHeaderMetaMap,
SettingsNavGroup,
SettingsNavGroupId,
SettingsNavItem,
SettingsTab,
} from './settingsNavigationModel';
@@ -559,5 +559,3 @@ Pulse prepares the first-host install token from setup so you can move straight
tokenName,
};
};
export type InfrastructureInstallState = ReturnType<typeof useInfrastructureInstallState>;
@@ -6,12 +6,6 @@ import { useInfrastructureDiscoveryRuntimeState } from './useInfrastructureDisco
import { useTrueNASSettingsPanelState } from './useTrueNASSettingsPanelState';
import { useVMwareSettingsPanelState } from './useVMwareSettingsPanelState';
export type {
DiscoveryScanStatus,
DiscoveredServer,
NodeType,
} from './infrastructureSettingsModel';
type InfrastructureEventBus = {
on<T extends EventType>(event: T, handler: (data?: EventDataMap[T]) => void): () => void;
};
@@ -1,3 +1,2 @@
export { SetupWizard } from './SetupWizard';
export { StepIndicator } from './StepIndicator';
export type { WizardState, WizardStep } from './SetupWizard';
export type { WizardState, } from './SetupWizard';
@@ -68,7 +68,6 @@ export const WORKLOAD_TABLE_HISTORY_RANGE_LABELS: Record<WorkloadTableMetricHist
'7d': '7d',
};
export const WORKLOAD_TABLE_HISTORY_DEFAULT_RANGE: WorkloadTableMetricHistoryRange = '1h';
export const WORKLOAD_TABLE_HISTORY_RANGE = WORKLOAD_TABLE_HISTORY_DEFAULT_RANGE;
export const WORKLOAD_TABLE_HISTORY_MAX_POINTS = 72;
export const WORKLOAD_TABLE_HISTORY_POLL_MS = 30_000;
@@ -88,7 +88,6 @@ export const DEFAULT_WORKLOADS_SORT_KEY: WorkloadsSortKey = 'type';
export const DEFAULT_WORKLOADS_SORT_DIRECTION = 'asc';
export const DEFAULT_WORKLOADS_VIEW_MODE: ViewMode = 'all';
export const DEFAULT_WORKLOADS_STATUS_MODE: WorkloadsStatusMode = 'all';
export const DEFAULT_WORKLOADS_GROUPING_MODE: WorkloadsGroupingMode = 'grouped';
export const DEFAULT_WORKLOADS_METRIC_DISPLAY_MODE: WorkloadsMetricDisplayMode = 'bars';
export const countActiveWorkloadsFilters = (
@@ -4,7 +4,6 @@ export { InvestigationSection } from './InvestigationSection';
export { InvestigationMessages } from './InvestigationMessages';
export { RemediationStatus } from './RemediationStatus';
export { RunToolCallTrace } from './RunToolCallTrace';
export { PatrolStatusBar } from './PatrolStatusBar';
export { RunHistoryEntry } from './RunHistoryEntry';
export { RunHistoryPanel } from './RunHistoryPanel';
export { CountdownTimer } from './CountdownTimer';
@@ -3,16 +3,13 @@ export { FilterChip } from './FilterChip';
export { AddFilterMenu } from './AddFilterMenu';
export { SavedViewsMenu } from './SavedViewsMenu';
export { useSavedViews } from './useSavedViews';
export type { SavedView, UseSavedViewsResult } from './useSavedViews';
export type { SavedView, } from './useSavedViews';
export {
clearFilter,
formatFilterChipValue,
isFilterSet,
} from './filterCatalog';
export type {
FilterBarProps,
FilterBarSearch,
FilterDef,
FilterGroupKey,
FilterSelectOption,
} from './filterCatalog';
@@ -10,7 +10,6 @@ import { useFilterButtonGroupState } from './useFilterButtonGroupState';
export type {
FilterButtonGroupProps,
FilterButtonGroupVariant,
FilterOption,
} from './filterButtonGroupModel';
@@ -16,7 +16,7 @@ import {
} from './pulseDataGridModel';
import { usePulseDataGridState } from './usePulseDataGridState';
export type { PulseDataGridProps, TableColumn } from './pulseDataGridModel';
export type { PulseDataGridProps, } from './pulseDataGridModel';
/**
* A standardized, responsive datagrid component for Pulse.
@@ -3,9 +3,6 @@ import { type SearchFieldProps } from './searchFieldModel';
import { useSearchFieldState } from './useSearchFieldState';
export type {
SearchFieldFocusEvent,
SearchFieldKeyboardEvent,
SearchFieldMouseEvent,
SearchFieldProps,
} from './searchFieldModel';
@@ -7,7 +7,7 @@ import {
import { type SearchInputProps } from './searchInputModel';
import { useSearchInputState } from './useSearchInputState';
export type { SearchInputKeyboardEvent, SearchInputProps } from './searchInputModel';
export type { SearchInputProps } from './searchInputModel';
export const SearchInput: Component<SearchInputProps> = (props) => {
const search = useSearchInputState(props);
@@ -11,7 +11,7 @@ import {
} from './searchTipsPopoverModel';
import { useSearchTipsPopoverState } from './useSearchTipsPopoverState';
export type { SearchTip, SearchTipsPopoverProps } from './searchTipsPopoverModel';
export type { SearchTipsPopoverProps } from './searchTipsPopoverModel';
export const SearchTipsPopover: Component<SearchTipsPopoverProps> = (props) => {
const triggerVariant = () => getSearchTipsPopoverTriggerVariant(props.triggerVariant);
@@ -11,9 +11,7 @@ import { useSelectionCardGroupState } from './useSelectionCardGroupState';
export type {
SelectionCardGroupProps,
SelectionCardGroupVariant,
SelectionCardOption,
SelectionCardTone,
} from './selectionCardGroupModel';
export function SelectionCardGroup<T extends string | number>(props: SelectionCardGroupProps<T>) {
@@ -153,85 +153,3 @@ export const ResponsiveMetricCell: Component<ResponsiveMetricCellProps> = (props
</Show>
);
};
/**
* Simpler metric text component for when you just want colored percentage
* without the MetricBar complexity
*/
export const MetricText: Component<{
value: number;
type: 'cpu' | 'memory' | 'disk';
label?: string;
class?: string;
thresholds?: MetricDisplayThresholds | null;
}> = (props) => {
const displayLabel = createMemo(() => props.label ?? formatPercent(props.value));
const colorClass = createMemo(() => metricTextClass(props.value, props.type, props.thresholds));
return (
<span class={`text-xs text-center ${colorClass()} ${props.class || ''}`}>
<Show when={!props.label} fallback={displayLabel()}>
<AnimatedNumber value={props.value} format={formatPercent} />
</Show>
</span>
);
};
/**
* Metric cell with explicit mobile/desktop rendering
* Use this when you need full control over what renders in each mode
*/
export const DualMetricCell: Component<{
value: number;
type: 'cpu' | 'memory' | 'disk';
label?: string;
sublabel?: string;
resourceId?: string;
isRunning?: boolean;
showMobile: boolean;
mobileContent?: JSX.Element;
desktopContent?: JSX.Element;
fallback?: JSX.Element;
class?: string;
thresholds?: MetricDisplayThresholds | null;
}> = (props) => {
const displayLabel = createMemo(() => props.label ?? formatPercent(props.value));
const colorClass = createMemo(() => metricTextClass(props.value, props.type, props.thresholds));
const isRunning = () => props.isRunning !== false;
const defaultFallback = (
<div class="h-4 flex items-center justify-center">
<span class="text-xs text-muted" aria-hidden="true"></span>
</div>
);
const defaultMobileContent = (
<div class={`text-xs text-center ${colorClass()}`}>
<Show when={!props.label} fallback={displayLabel()}>
<AnimatedNumber value={props.value} format={formatPercent} />
</Show>
</div>
);
const defaultDesktopContent = (
<MetricBar
value={props.value}
label={displayLabel()}
animatedLabelValue={props.label ? undefined : props.value}
sublabel={props.sublabel}
type={props.type}
resourceId={props.resourceId}
thresholds={props.thresholds}
/>
);
return (
<Show when={isRunning()} fallback={props.fallback ?? defaultFallback}>
<div class={props.class}>
<Show when={props.showMobile} fallback={props.desktopContent ?? defaultDesktopContent}>
{props.mobileContent ?? defaultMobileContent}
</Show>
</div>
</Show>
);
};
@@ -1,3 +1,3 @@
export const SUMMARY_CHART_SLOT_CLASS = 'h-[136px] sm:h-[150px]';
export const SUMMARY_CHART_SLOT_COMPACT_CLASS = 'h-[108px] sm:h-[120px]';
export const SUMMARY_CHART_PLOT_AREA_CLASS = 'h-[120px] sm:h-[134px]';
@@ -8,6 +8,3 @@ export const SUMMARY_TIME_RANGE_LABEL: Record<SummaryTimeRange, string> = {
'24h': '24h',
'7d': '7d',
};
export const isSummaryTimeRange = (value: string): value is SummaryTimeRange =>
(SUMMARY_TIME_RANGES as readonly string[]).includes(value);
@@ -73,5 +73,3 @@ export function useCommandPaletteState(props: CommandPaletteModalProps) {
setQuery,
};
}
export type CommandPaletteState = ReturnType<typeof useCommandPaletteState>;
@@ -126,5 +126,3 @@ export function useContainerUpdateButtonState(props: UpdateButtonProps) {
shouldHideButton,
};
}
export type ContainerUpdateButtonState = ReturnType<typeof useContainerUpdateButtonState>;
@@ -98,5 +98,3 @@ export function useHelpIconState(props: HelpIconProps) {
toggleOpen,
};
}
export type HelpIconState = ReturnType<typeof useHelpIconState>;
@@ -80,5 +80,3 @@ export function useMobileNavBarState(props: MobileNavBarProps) {
showLeftFade,
};
}
export type MobileNavBarState = ReturnType<typeof useMobileNavBarState>;
@@ -211,5 +211,3 @@ export function useWebInterfaceUrlFieldState(props: WebInterfaceUrlFieldProps) {
urlValue,
};
}
export type WebInterfaceUrlFieldState = ReturnType<typeof useWebInterfaceUrlFieldState>;
@@ -124,16 +124,6 @@ export const getGuestOverrideIdentity = (
};
};
export const canonicalGuestOverrideResourceId = (
resource: GuestOverrideResourceLike,
): string | undefined => {
const identity = getGuestOverrideIdentity(resource);
if (!identity) {
return undefined;
}
return `${identity.instance}:${identity.node}:${identity.vmid}`;
};
export const guestOverrideStorageId = (resource: GuestOverrideResourceLike): string => {
const identity = getGuestOverrideIdentity(resource);
if (!identity) {
@@ -40,17 +40,3 @@ export const stripStateKeys = (
delete (next as Record<string, unknown>).poweredOffSeverity;
return next;
};
export const removeOverrideState = (
overrides: Override[],
rawOverridesConfig: Record<string, RawOverrideConfig>,
resourceId: string,
) => {
const nextRawConfig = { ...rawOverridesConfig };
delete nextRawConfig[resourceId];
return {
nextOverrides: overrides.filter((override) => override.id !== resourceId),
nextRawConfig,
};
};
@@ -81,14 +81,6 @@ export const getStoragePoolTableColumns = (
colClassName: 'hidden xl:table-column xl:w-[11%]',
},
];
export const STORAGE_CONTROLS_NODE_SELECT_CLASS =
'px-2 py-1 text-xs border border-border rounded-md bg-surface text-base-content focus:ring-2 focus:ring-blue-500 focus:border-blue-500';
export const STORAGE_CONTROLS_NODE_DIVIDER_CLASS = 'h-5 w-px bg-surface-hover hidden sm:block';
export const STORAGE_CONTENT_CARD_HEADER_CLASS =
'border-b border-border bg-surface-hover px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-muted';
export const STORAGE_CONTENT_CARD_BODY_CLASS = 'p-2';
export const STORAGE_POOLS_EMPTY_STATE_CLASS = 'p-6 text-sm text-muted';
@@ -854,18 +854,6 @@ const matchesTrueNASStorageSearch = (resource: Resource, search: string): boolea
return haystack.includes(needle);
};
export function filterTrueNASStorageResources(
resources: Resource[],
search: string,
status: TrueNASStorageStatusFilter,
): Resource[] {
return resources.filter((resource) => {
if (!matchesTrueNASStorageSearch(resource, search)) return false;
if (status === 'all') return true;
return mapTrueNASStorageStatus(resource) === status;
});
}
const resourceIncidentLabel = (resource: Resource, incident: ResourceIncident): string => {
const label = asTrimmedString(resource.incidentLabel);
if (label) return label;
@@ -29,7 +29,6 @@ export const TRUENAS_DEFAULT_TAB = 'overview';
export const VMWARE_PATH = '/vmware';
export const VMWARE_DEFAULT_TAB = 'overview';
export const PMG_THRESHOLDS_PATH = '/alerts/thresholds/mail-gateway';
export const ALERTS_OVERVIEW_PATH = '/alerts/overview';
export const PATROL_PATH = '/patrol';
export const STORAGE_QUERY_PARAMS = {
-21
View File
@@ -191,18 +191,6 @@ export interface AIConversationMessage {
content: string;
}
// AI Execute request/response types
export interface AIExecuteRequest {
prompt: string;
target_type?: string; // "agent", "system-container", "vm", etc.
target_id?: string;
context?: Record<string, unknown>;
history?: AIConversationMessage[]; // Previous conversation messages
finding_id?: string; // If fixing a patrol finding, the ID to resolve on success
model?: string; // Override model for this request (user selection in chat)
use_case?: 'chat' | 'patrol'; // Optional server-side routing/model selection
}
// Tool execution info
export interface AIToolExecution {
name: string; // "run_command", "read_file"
@@ -387,15 +375,6 @@ export interface AIChatMessage {
toolCalls?: AIChatToolCall[];
}
export interface AIChatSession {
id: string;
username: string;
title: string;
createdAt: Date;
updatedAt: Date;
messages: AIChatMessage[];
}
// Summary returned by list endpoint (no messages)
export interface AIChatSessionHandoffResource {
id?: string;
-33
View File
@@ -63,30 +63,6 @@ export interface ConnectedInfrastructureItem {
surfaces: ConnectedInfrastructureSurface[];
}
export interface KubernetesCluster {
id: string;
agentId: string;
name?: string;
displayName?: string;
customDisplayName?: string;
server?: string;
context?: string;
version?: string;
status: string;
lastSeen: number;
intervalSeconds: number;
agentVersion?: string;
tokenId?: string;
tokenName?: string;
tokenHint?: string;
tokenLastUsedAt?: number;
hidden?: boolean;
pendingUninstall?: boolean;
nodes?: KubernetesNode[];
pods?: KubernetesPod[];
deployments?: KubernetesDeployment[];
}
export interface KubernetesNode {
uid: string;
name: string;
@@ -1068,11 +1044,6 @@ export interface PhysicalDisk {
smartAttributes?: SMARTAttributes;
}
/** Returns the best resource ID for disk metrics queries (serial preferred, WWN fallback). */
export function diskResourceId(disk: PhysicalDisk): string | null {
return disk.serial || disk.wwn || null;
}
export interface CPUInfo {
model: string;
cores: number;
@@ -1259,7 +1230,3 @@ export type WSMessage =
scanning?: boolean;
};
};
// Utility types
export type Status = 'running' | 'stopped' | 'paused' | 'unknown';
export type GuestType = 'vm' | 'system-container';
-53
View File
@@ -87,15 +87,6 @@ export interface NodesConfig {
pbsInstances: PBSNodeConfig[];
}
/**
* Complete configuration structure
*/
export interface PulseConfig {
auth: Partial<AuthConfig>; // From .env
system: SystemConfig; // From system.json
nodes: NodesConfig; // From nodes.enc
}
/**
* API response for security status
*/
@@ -180,47 +171,3 @@ export interface SSOProviderInfo {
iconUrl?: string;
loginUrl: string;
}
/**
* First-run setup request
*/
export interface SetupRequest {
username: string;
password: string;
apiToken?: string;
enableNotifications?: boolean;
darkMode?: boolean;
}
/**
* Type guards for configuration validation
*/
export const isValidUpdateChannel = (value: string): value is UpdateChannel => {
return value === 'stable' || value === 'rc';
};
export const isValidTimeFormat = (value: string): boolean => {
return /^([01]\d|2[0-3]):([0-5]\d)$/.test(value);
};
/**
* Default values for configuration
*/
export const DEFAULT_CONFIG: {
system: SystemConfig;
} = {
system: {
connectionTimeout: 60,
autoUpdateEnabled: false,
updateChannel: 'stable',
autoUpdateCheckInterval: 24,
autoUpdateTime: '03:00',
backupPollingEnabled: true,
backupPollingInterval: 0,
temperatureMonitoringEnabled: true,
telemetryEnabled: true,
sshPort: 22,
allowedOrigins: '',
frontendPort: 7655,
},
};
-4
View File
@@ -162,10 +162,6 @@ export interface UpdateNotesRequest {
user_secrets?: Record<string, string>;
}
export interface UpdateSettingsRequest {
max_discovery_age_days?: number; // Days before rediscovery (default 30)
}
// AI provider information for discovery transparency
export interface AIProviderInfo {
provider: string; // e.g., "anthropic", "openai", "ollama"
-15
View File
@@ -104,18 +104,3 @@ export type NodeConfigWithStatus = NodeConfig & {
hasToken?: boolean;
status: 'connected' | 'disconnected' | 'offline' | 'error' | 'pending';
};
export interface NodesResponse {
pve_instances: PVENodeConfig[];
pbs_instances: PBSNodeConfig[];
pmg_instances?: PMGNodeConfig[];
}
export interface NodeUpdateRequest {
node: NodeConfig;
}
export interface NodeDeleteResponse {
success: boolean;
message: string;
}
-8
View File
@@ -137,10 +137,6 @@ export interface RecoveryPointsSeriesBucket {
remote: number;
}
export interface RecoveryPointsSeriesResponse {
data: RecoveryPointsSeriesBucket[];
}
export interface RecoveryPointsFacets {
clusters?: string[];
nodesAgents?: string[];
@@ -150,7 +146,3 @@ export interface RecoveryPointsFacets {
hasVerification?: boolean;
hasEntityId?: boolean;
}
export interface RecoveryPointsFacetsResponse {
data: RecoveryPointsFacets;
}
-2
View File
@@ -196,8 +196,6 @@ export const requiresGovernedResourceDisplay = (policy?: ResourcePolicy | null):
if (!policy) return false;
return policy.routing.scope === 'local-only' || (policy.routing.redact?.length ?? 0) > 0;
};
export type ResourceApprovalLevel = 'none' | 'dry_run_only' | 'admin' | 'mfa';
export type ResourceChangeConfidence = 'high' | 'medium' | 'low';
export type ResourceChangeKind =
| 'state_transition'
@@ -342,23 +342,6 @@ export const getFindingSeverityCompactLabel = (
severity: UnifiedFinding['severity'] | string,
): string => FINDING_SEVERITY_COMPACT_LABELS[severity] || String(severity).toUpperCase();
export const getFindingCompactBadgePresentation = (
finding: Pick<UnifiedFinding, 'severity' | 'resourceId' | 'resourceName' | 'title'>,
): FindingCompactBadgePresentation => {
if (isPatrolRuntimeFinding(finding)) {
const severityPresentation = getFindingSeverityPresentation(finding);
return {
label: severityPresentation.label,
badgeClasses: severityPresentation.badgeClasses,
};
}
return {
label: getFindingSeverityCompactLabel(finding.severity),
badgeClasses: getFindingSeverityBadgeClasses(finding.severity),
};
};
export const isPatrolRuntimeFinding = (
finding: Pick<UnifiedFinding, 'resourceId' | 'resourceName' | 'title'>,
): boolean => {
@@ -617,25 +600,6 @@ export const doesFindingNeedAttention = (
);
};
// True when the finding has an investigation outcome indicating that some
// remediation step has run against it — anything past "fix queued." For these
// states, Verify fix is a meaningful action; for fix_queued (still awaiting
// approval) and earlier states there is nothing applied yet to verify, and
// for fix_failed the fix didn't complete so verification doesn't apply.
export const findingHasAppliedFix = (
finding: Pick<UnifiedFinding, 'investigationOutcome'>,
): boolean => {
switch (finding.investigationOutcome) {
case 'fix_executed':
case 'fix_verified':
case 'fix_verification_failed':
case 'fix_verification_unknown':
return true;
default:
return false;
}
};
export const getFindingLoopStateBadgeClasses = (loopState: string): string =>
FINDING_LOOP_STATE_CLASSES[loopState] || DEFAULT_LOOP_STATE_CLASSES;
@@ -190,10 +190,6 @@ export function getAlertConfigDiscardLabel(isReloading: boolean) {
return isReloading ? ALERT_CONFIG_DISCARDING_LABEL : ALERT_CONFIG_DISCARD_LABEL;
}
export function getAlertConfigSwarmGapValidationError() {
return ALERT_CONFIG_SWARM_GAP_VALIDATION;
}
export function getAlertConfigQuietHourSuppressOptions() {
return ALERT_CONFIG_QUIET_HOUR_SUPPRESS_OPTIONS;
}
@@ -275,17 +275,6 @@ export function getAlertWebhookUrlPlaceholder(urlPattern?: string) {
return urlPattern || ALERT_WEBHOOK_URL_PLACEHOLDER;
}
export function getAlertWebhookMentionPlaceholder(service: string) {
return (
ALERT_WEBHOOK_SERVICE_PRESENTATION[service as AlertWebhookService]?.mentionPlaceholder ||
ALERT_WEBHOOK_MENTION_FALLBACK_PLACEHOLDER
);
}
export function getAlertWebhookMentionHelp(service: string) {
return ALERT_WEBHOOK_SERVICE_PRESENTATION[service as AlertWebhookService]?.mentionHelp || '';
}
export function getAlertWebhookSummaryLabel(enabledCount: number, totalCount: number) {
return `${enabledCount} of ${totalCount} webhooks enabled`;
}
-4
View File
@@ -146,10 +146,6 @@ export function parseCloudTier(value?: string | null): CloudTierKey {
}
}
export function getCloudPlanForTier(value?: string | null): CloudPlanDefinition {
return CLOUD_PLAN_BY_TIER[parseCloudTier(value)];
}
export function getCloudPlanPricePresentation(
plan: CloudPlanDefinition,
): CloudPlanPricePresentation {
@@ -48,8 +48,6 @@ export interface HostedCommercialModelInput {
guestUsage: number;
renewsOrExpires: string;
}
export const SELF_HOSTED_NOT_METERED_LABEL = 'Not metered';
export const LIFETIME_DAYS_REMAINING_LABEL = 'Permanent';
const asUnlimitedLimit = (value?: number) =>
@@ -307,14 +307,6 @@ export function readInfrastructureSummaryCache(
}
}
export function hasFreshInfrastructureSummaryCache(
range: TimeRange,
maxAgeMs: number = INFRA_SUMMARY_CACHE_MAX_AGE_MS,
metrics?: readonly InfrastructureSummaryMetric[] | null,
): boolean {
return readInfrastructureSummaryCache(range, maxAgeMs, undefined, metrics) !== null;
}
const inFlightFetches = new Map<string, Promise<InfrastructureSummaryFetchResult>>();
let infraSummaryFetchSeq = 0;
@@ -60,10 +60,6 @@ export interface LicenseInlineNotice {
body: string;
}
export interface LicenseActionNotice extends LicenseInlineNotice {
actionLabel: string;
}
export interface BillingAdminOrganizationBadge {
label: string;
badgeClass: string;
@@ -758,13 +758,3 @@ export function getContainerRuntimeBadgeForRuntime(runtime?: string | null): Res
title: `Runtime: ${label}`,
};
}
export function getContainerRuntimeBadge(
platformType?: PlatformType,
platformData?: Record<string, unknown> | null,
): ResourceBadge | null {
if (platformType !== 'docker' || !platformData) return null;
const docker = (platformData as { docker?: { runtime?: string } } | undefined)?.docker;
return getContainerRuntimeBadgeForRuntime(docker?.runtime);
}
@@ -163,9 +163,3 @@ export const buildStorageSourceOptionsFromKeys = (
const orderedKeys = orderStorageSourceKeys(keys).filter((key) => key !== 'all');
return [ALL_STORAGE_SOURCE_OPTION, ...orderedKeys.map((key) => getStorageSourceOption(key))];
};
export const DEFAULT_STORAGE_SOURCE_OPTIONS: StorageSourceOption[] =
buildStorageSourceOptionsFromKeys(['proxmox-pve', 'proxmox-pbs', 'ceph', 'truenas']);
export const buildStorageSourceOptions = (storageList: Storage[]): StorageSourceOption[] =>
buildStorageSourceOptionsFromKeys(storageList.map((storage) => resolveStorageSourceKey(storage)));
@@ -48,11 +48,6 @@ export function fetchStorageSummaryAndCache(
return request;
}
export function __resetStorageSummaryCacheForTests(): void {
inMemoryCache.clear();
inFlightFetches.clear();
}
const unsubscribeStorageOrgSwitch = eventBus.on('org_switched', () => {
inMemoryCache.clear();
inFlightFetches.clear();
@@ -39,12 +39,6 @@ export function getUnifiedAgentLastSeenLabel(
return row.lastSeen ? formatRelativeTime(row.lastSeen) : '—';
}
export function getMonitoringStoppedEmptyState(hasFilters: boolean): string {
return hasFilters
? 'No monitoring-stopped items match the current filters.'
: 'No infrastructure currently has monitoring stopped.';
}
export function getUnifiedAgentStopMonitoringUnavailableMessage(): string {
return 'No host identifiers are available to stop monitoring.';
}
@@ -13,7 +13,6 @@ export interface UnifiedAgentLookupStatusPresentation {
}
export const MONITORING_STOPPED_STATUS_LABEL = 'Monitoring stopped';
export const ALLOW_RECONNECT_LABEL = 'Allow reconnect';
export function getUnifiedAgentStatusPresentation(
state: UnifiedAgentMonitoringState,
@@ -9,7 +9,6 @@ import { normalizeOrgScope } from '@/utils/orgScope';
import { eventBus } from '@/stores/events';
export const WORKLOADS_SUMMARY_CACHE_VERSION = 6;
export const WORKLOAD_CHART_DEFAULT_POINT_LIMIT = 180;
const WORKLOADS_SUMMARY_CACHE_PREFIX = 'pulse.workloadsSummaryCharts.';
const WORKLOADS_SUMMARY_CACHE_MAX_AGE_MS = 5 * 60_000;
+9 -26
View File
@@ -21,16 +21,16 @@ const (
FieldContainerStatus PostconditionField = "status"
// Systemd unit fields read via DBus or systemctl show.
FieldUnitActiveState PostconditionField = "ActiveState"
FieldUnitSubState PostconditionField = "SubState"
FieldUnitActiveEnterTimestamp PostconditionField = "ActiveEnterTimestamp"
FieldUnitActiveState PostconditionField = "ActiveState"
FieldUnitSubState PostconditionField = "SubState"
FieldUnitActiveEnterTimestamp PostconditionField = "ActiveEnterTimestamp"
// Docker container fields.
FieldDockerStatus PostconditionField = "status"
FieldDockerLastStarted PostconditionField = "last_started"
// Kubernetes deployment fields.
FieldDeploymentReadyReplicas PostconditionField = "readyReplicas"
FieldDeploymentReadyReplicas PostconditionField = "readyReplicas"
FieldDeploymentDesiredReplicas PostconditionField = "desiredReplicas"
)
@@ -69,11 +69,11 @@ type PostconditionCheck struct {
// postcondition to be observed; capabilities with no natural settle
// (a single-shot status read) use the default.
type CapabilityPostcondition struct {
Capability string `json:"capability"`
VerifyRead string `json:"verifyRead"`
Window time.Duration `json:"window"`
Description string `json:"description"`
Checks []PostconditionCheck `json:"checks"`
Capability string `json:"capability"`
VerifyRead string `json:"verifyRead"`
Window time.Duration `json:"window"`
Description string `json:"description"`
Checks []PostconditionCheck `json:"checks"`
}
// defaultVerifyWindow is the per-capability fallback window. The agentexec
@@ -82,23 +82,6 @@ type CapabilityPostcondition struct {
// this surface" hint.
const defaultVerifyWindow = 2 * time.Minute
// CapabilityPostconditions returns the canonical capability -> postcondition
// map for the verifier substrate. The returned map is a copy so callers
// cannot mutate the shared substrate.
//
// Capabilities not present in this map have no automated postcondition and
// will surface as VerificationUnknown on the audit record.
func CapabilityPostconditions() map[string]CapabilityPostcondition {
out := make(map[string]CapabilityPostcondition, len(capabilityPostconditions))
for k, v := range capabilityPostconditions {
copyChecks := make([]PostconditionCheck, len(v.Checks))
copy(copyChecks, v.Checks)
v.Checks = copyChecks
out[k] = v
}
return out
}
// LookupCapabilityPostcondition returns the postcondition for the given
// capability name, or false if no postcondition is registered.
func LookupCapabilityPostcondition(capability string) (CapabilityPostcondition, bool) {
-8
View File
@@ -333,14 +333,6 @@ func (u *Updater) CheckAndUpdate(ctx context.Context) {
u.logger.Info().Msg("agent updated successfully, restarting")
}
// setAuthHeaders adds authentication headers to the request if an API token is configured.
func (u *Updater) setAuthHeaders(req *http.Request) {
if u.cfg.APIToken != "" {
req.Header.Set(apiTokenHeader, u.cfg.APIToken)
req.Header.Set(authorizationHeader, bearerTokenPrefix+u.cfg.APIToken)
}
}
func (u *Updater) startCheck() bool {
u.checkMu.Lock()
defer u.checkMu.Unlock()
-6
View File
@@ -85,12 +85,6 @@ func sessionFSMState(fsm *SessionFSM) string {
return string(fsm.State)
}
func (a *AgenticLoop) currentFSMState() string {
a.mu.Lock()
defer a.mu.Unlock()
return sessionFSMState(a.sessionFSM)
}
func fallbackProviderStreamErrorMessage(err error) string {
const defaultMessage = "AI response stream interrupted before completion. Please retry."
if err == nil {
-36
View File
@@ -50,26 +50,6 @@ type mockBaselineStore struct {
anomalies map[string]baselineResult
}
func (m *mockBaselineStore) CheckAnomaly(resourceID, metric string, value float64) (baseline.AnomalySeverity, float64, *baseline.MetricBaseline) {
res, ok := m.anomalies[resourceID+":"+metric]
if !ok {
return baseline.AnomalyNone, 0, &baseline.MetricBaseline{}
}
return res.severity, res.zScore, res.bl
}
func (m *mockBaselineStore) GetBaseline(resourceID, metric string) (*baseline.MetricBaseline, bool) {
res, ok := m.anomalies[resourceID+":"+metric]
if !ok {
return nil, false
}
return res.bl, true
}
func (m *mockBaselineStore) Update(resourceID, metric string, value float64) {}
func (m *mockBaselineStore) Save() error { return nil }
func (m *mockBaselineStore) Load() error { return nil }
func TestGenerateRemediationSummary(t *testing.T) {
tests := []struct {
command string
@@ -643,19 +623,3 @@ func TestService_BuildRecentResourceChangesContext_FallsBackToMemoryFormatter(t
type mockIncidentStore struct {
}
func (m *mockIncidentStore) FormatForAlert(alertID string, limit int) string {
return "alert:" + alertID
}
func (m *mockIncidentStore) FormatForResource(resourceID string, limit int) string {
return "res:" + resourceID
}
func (m *mockIncidentStore) FormatForPatrol(limit int) string {
return "patrol"
}
func (m *mockIncidentStore) Record(resourceID, resourceType, alertID, analysis, remediation string) error {
return nil
}
-11
View File
@@ -157,14 +157,3 @@ func cloneStringSlice(values []string) []string {
copy(cloned, values)
return cloned
}
func cloneStringMap(values map[string]string) map[string]string {
if len(values) == 0 {
return nil
}
cloned := make(map[string]string, len(values))
for k, v := range values {
cloned[k] = v
}
return cloned
}
-38
View File
@@ -194,13 +194,6 @@ func normalizeConfig(cfg Config) Config {
return cfg
}
func normalizeContext(ctx context.Context) context.Context {
if ctx == nil {
return context.Background()
}
return ctx
}
// NewService creates a new infrastructure discovery service.
func NewService(knowledgeStore *knowledge.Store, cfg Config) *Service {
cfg = normalizeConfig(cfg)
@@ -245,21 +238,6 @@ func (s *Service) SetReadState(rs unifiedresources.ReadState) {
}
}
// goRecover launches fn in a goroutine with panic recovery logging.
func goRecover(label string, fn func()) {
go func() {
defer func() {
if r := recover(); r != nil {
log.Error().
Interface("panic", r).
Stack().
Msgf("Recovered from panic in %s", label)
}
}()
fn()
}()
}
// Start begins the background discovery service.
func (s *Service) Start(ctx context.Context) {
if ctx == nil {
@@ -914,22 +892,6 @@ func (s *Service) saveDiscoveries(apps []DiscoveredApp) {
}
}
func (s *Service) tryStartDiscovery() bool {
s.discoveryMu.Lock()
defer s.discoveryMu.Unlock()
if s.discoveryRun {
return false
}
s.discoveryRun = true
return true
}
func (s *Service) finishDiscovery() {
s.discoveryMu.Lock()
s.discoveryRun = false
s.discoveryMu.Unlock()
}
// GetDiscoveries returns the cached list of discovered applications.
func (s *Service) GetDiscoveries() []DiscoveredApp {
s.mu.RLock()
-9
View File
@@ -1315,15 +1315,6 @@ func (i *Intelligence) getResourcesAtRisk(limit int) []ResourceRiskSummary {
return summaries
}
func (i *Intelligence) detectCurrentAnomalies(resourceID string) []AnomalyReport {
if i.anomalyDetector != nil {
return i.anomalyDetector(resourceID)
}
// This would be called with current metrics from state
// For now, return empty - will be integrated with patrol
return nil
}
func (i *Intelligence) getRecentChangesForResource(resourceID string, limit int) []unifiedresources.ResourceChange {
resourceID = strings.TrimSpace(resourceID)
if resourceID == "" || limit <= 0 {
-35
View File
@@ -174,41 +174,6 @@ func (d *ChangeDetector) DetectChanges(currentSnapshots []ResourceSnapshot) []Ch
return newChanges
}
func (d *ChangeDetector) requestAsyncSave() {
if d.dataDir == "" {
return
}
d.saveStateMu.Lock()
d.saveRequested = true
if d.saveRunning {
d.saveStateMu.Unlock()
return
}
d.saveRunning = true
d.saveStateMu.Unlock()
go d.runSaveLoop()
}
func (d *ChangeDetector) runSaveLoop() {
for {
d.saveStateMu.Lock()
shouldSave := d.saveRequested
d.saveRequested = false
if !shouldSave {
d.saveRunning = false
d.saveStateMu.Unlock()
return
}
d.saveStateMu.Unlock()
if err := d.saveToDisk(); err != nil {
log.Warn().Err(err).Msg("Failed to save change history")
}
}
}
// detectResourceChanges checks for changes between two snapshots of the same resource
func (d *ChangeDetector) detectResourceChanges(prev, current ResourceSnapshot, now time.Time) []Change {
var changes []Change
-35
View File
@@ -85,16 +85,6 @@ type ContextStoreConfig struct {
RelevanceDecayDays int // Days after which relevance starts decaying
}
// DefaultContextStoreConfig returns sensible defaults
func DefaultContextStoreConfig() ContextStoreConfig {
return ContextStoreConfig{
MaxMemoriesPerType: 1000,
MaxResourceNotes: 20,
RetentionDays: 90,
RelevanceDecayDays: 7,
}
}
// ContextStore stores and manages persistent AI context
type ContextStore struct {
mu sync.RWMutex
@@ -370,31 +360,6 @@ func (s *ContextStore) Recall(resourceID string) []Memory {
return result
}
// RecallByType retrieves memories of a specific type
func (s *ContextStore) RecallByType(memType MemoryType, limit int) []Memory {
s.mu.Lock()
defer s.mu.Unlock()
var result []Memory
if memories, ok := s.memories[memType]; ok {
for _, mem := range memories {
s.markUsedLocked(mem)
result = append(result, *mem)
}
}
// Sort by relevance
sort.Slice(result, func(i, j int) bool {
return result[i].Relevance > result[j].Relevance
})
if limit > 0 && len(result) > limit {
result = result[:limit]
}
return result
}
// GetResourceMemory returns the memory for a specific resource
func (s *ContextStore) GetResourceMemory(resourceID string) *ResourceMemory {
s.mu.RLock()
-7
View File
@@ -1253,13 +1253,6 @@ func maxInt(a, b int) int {
return b
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
func (p *PatrolService) assembleSeedWithinBudget(sections []seedSection, budgetTokens int) string {
if len(sections) == 0 {
return ""
-4
View File
@@ -1872,10 +1872,6 @@ func patrolRegisterResourceMetrics(dest map[string]map[string]float64, metrics m
}
}
func patrolGuestMatches(guestID, id, name string, vmid int) bool {
return id == guestID || name == guestID || fmt.Sprintf("%d", vmid) == guestID
}
func patrolFirstIP(ips []string) string {
if len(ips) == 0 {
return ""
-11
View File
@@ -419,17 +419,6 @@ func patrolRuntimeSortedResourceIDs(s patrolRuntimeState) []string {
return ids
}
func patrolRuntimeStorageResourceCount(s patrolRuntimeState) int {
count := 0
patrolVisitRuntimeResources(s, func(record patrolRuntimeResourceRecord) bool {
if record.kind == patrolRuntimeResourceStorage || record.kind == patrolRuntimeResourcePhysicalDisk {
count++
}
return true
})
return count
}
type patrolRuntimeResourceCounts struct {
nodes int
guests int
-8
View File
@@ -180,14 +180,6 @@ func (c *OpenAIClient) shouldSendReasoningContent() bool {
return c.isDeepSeek()
}
func normalizeOpenAICompatibleModelName(model string) string {
model = strings.ToLower(strings.TrimSpace(model))
for _, prefix := range []string{"openai:", "openrouter:", "deepseek:"} {
model = strings.TrimPrefix(model, prefix)
}
return model
}
func (c *OpenAIClient) applyProviderHeaders(req *http.Request) {
if !c.isOpenRouter() {
return
-4
View File
@@ -127,10 +127,6 @@ type ChatResponse struct {
OutputTokens int `json:"output_tokens,omitempty"`
}
func EmptyChatResponse() ChatResponse {
return ChatResponse{}.NormalizeCollections()
}
func (r ChatResponse) NormalizeCollections() ChatResponse {
if r.ToolCalls == nil {
r.ToolCalls = []ToolCall{}
-12
View File
@@ -2,18 +2,6 @@ package tools
import "sync"
// KnownToolNames returns the canonical list of registered Pulse tool names,
// keyed off the registry built by registerTools(). The list is built lazily
// on first call via a throwaway executor so adding a new tool to
// registerTools() automatically extends the allowlist — no separate
// hand-maintained list to drift out of sync.
func KnownToolNames() []string {
initKnownToolNames()
out := make([]string, len(knownToolNamesList))
copy(out, knownToolNamesList)
return out
}
// IsKnownToolName reports whether name is one of the canonical Pulse tool
// names. Used by chat-content sanitisers to gate stripping on a closed
// allowlist (e.g. JSON tool-call shapes leaked by weak local models that
-31
View File
@@ -238,23 +238,6 @@ const (
ErrCodeNoAgent = "NO_AGENT"
)
// NewToolSuccess creates a successful tool response
func NewToolSuccess(data interface{}) ToolResponse {
return ToolResponse{
OK: true,
Data: data,
}
}
// NewToolSuccessWithMeta creates a successful tool response with metadata
func NewToolSuccessWithMeta(data interface{}, meta map[string]interface{}) ToolResponse {
return ToolResponse{
OK: true,
Data: data,
Meta: meta,
}
}
// NewToolBlockedError creates a policy/validation blocked error
func NewToolBlockedError(code, message string, details map[string]interface{}) ToolResponse {
return ToolResponse{
@@ -268,20 +251,6 @@ func NewToolBlockedError(code, message string, details map[string]interface{}) T
}
}
// NewToolFailedError creates a runtime failure error
func NewToolFailedError(code, message string, retryable bool, details map[string]interface{}) ToolResponse {
return ToolResponse{
OK: false,
Error: &ToolError{
Code: code,
Message: message,
Failed: true,
Retryable: retryable,
Details: details,
},
}
}
// Helper functions
// NewTextContent creates a text content object
-18
View File
@@ -1581,24 +1581,6 @@ func extractServiceUnitName(command string) string {
return ""
}
// extractContainerName parses the container name out of a docker/podman
// command of the form "docker restart <name>" or "podman stop <name>".
func extractContainerName(command string) string {
fields := strings.Fields(strings.TrimSpace(command))
if len(fields) < 3 {
return ""
}
first := strings.ToLower(fields[0])
if first != "docker" && first != "podman" {
return ""
}
verb := strings.ToLower(fields[1])
if verb != "restart" && verb != "stop" && verb != "start" {
return ""
}
return fields[2]
}
// shellQuoteSingle wraps the value in single quotes and escapes any
// embedded single quotes for shell-safe inclusion in the verification
// command. The verification path runs through the same agent dispatch as
-4
View File
@@ -28,10 +28,6 @@ type stubDiskHealthProvider struct {
hosts []*unifiedresources.HostView
}
func (s *stubDiskHealthProvider) GetHosts() []*unifiedresources.HostView {
return s.hosts
}
type stubUpdatesProvider struct {
pending []ContainerUpdateInfo
enabled bool
-12
View File
@@ -39,18 +39,6 @@ func newSummarizeTestEnvironment(t *testing.T) (*PulseToolExecutor, func()) {
return exec, cleanup
}
func writeMetricSamples(t *testing.T, dir string, store *metrics.Store, resourceID string, value float64, count int) {
t.Helper()
now := time.Now()
for i := 0; i < count; i++ {
ts := now.Add(time.Duration(-30+i*2) * time.Minute)
store.Write("node", resourceID, "cpu", value, ts)
store.Write("node", resourceID, "memory", value-10, ts)
}
store.Flush()
_ = dir
}
func TestSummarizeTool_RegisteredAndDiscoverable(t *testing.T) {
exec, cleanup := newSummarizeTestEnvironment(t)
defer cleanup()
-15
View File
@@ -91,21 +91,6 @@ func (i *Integration) SetPatrolTrigger(fn PatrolTriggerFunc) {
i.bridge.SetPatrolTrigger(fn)
}
// Start starts the unified system
func (i *Integration) Start() {
i.bridge.Start()
log.Info().Msg("unified alert/finding system started")
}
// Stop stops the unified system
func (i *Integration) Stop() {
i.bridge.Stop()
if err := i.store.ForceSave(); err != nil {
log.Error().Err(err).Msg("failed to save unified findings on shutdown")
}
log.Info().Msg("unified alert/finding system stopped")
}
// GetStore returns the unified store
func (i *Integration) GetStore() *UnifiedStore {
return i.store
-11
View File
@@ -198,13 +198,6 @@ func (b *callbackBus) alertForAICallbacks() []func(alert *Alert) {
return callbacks
}
func (b *callbackBus) resolvedCallback() func(alertID string) {
b.mu.RLock()
cb := b.onResolved
b.mu.RUnlock()
return cb
}
func (b *callbackBus) resolvedCallbacks() []func(alertID string) {
b.mu.RLock()
defer b.mu.RUnlock()
@@ -330,10 +323,6 @@ func (m *Manager) getAlertForAICallbacks() []func(alert *Alert) {
return m.callbacks.alertForAICallbacks()
}
func (m *Manager) getResolvedCallback() func(alertID string) {
return m.callbacks.resolvedCallback()
}
func (m *Manager) getResolvedCallbacks() []func(alertID string) {
return m.callbacks.resolvedCallbacks()
}
-21
View File
@@ -66,28 +66,7 @@ func ensureValidHysteresis(threshold *HysteresisThreshold, metricName string) {
alertconfig.EnsureValidHysteresis(threshold, metricName)
}
func normalizeStorageDefaults(config *AlertConfig) { alertconfig.NormalizeStorageDefaults(config) }
func normalizeDockerThreshold(th HysteresisThreshold, defaultTrigger float64, metricName string) HysteresisThreshold {
return alertconfig.NormalizeDockerThreshold(th, defaultTrigger, metricName)
}
func normalizeDockerDefaults(config *AlertConfig) { alertconfig.NormalizeDockerDefaults(config) }
func normalizePMGDefaults(config *AlertConfig) { alertconfig.NormalizePMGDefaults(config) }
func normalizeSnapshotDefaults(config *AlertConfig) { alertconfig.NormalizeSnapshotDefaults(config) }
func normalizeBackupDefaults(config *AlertConfig) { alertconfig.NormalizeBackupDefaults(config) }
func normalizeNodeDefaults(config *AlertConfig) { alertconfig.NormalizeNodeDefaults(config) }
func normalizeAgentDefaults(config *AlertConfig) { alertconfig.NormalizeAgentDefaults(config) }
func normalizeKubernetesDefaults(config *AlertConfig) {
alertconfig.NormalizeKubernetesDefaults(config)
}
func normalizeTrueNASDefaults(config *AlertConfig) { alertconfig.NormalizeTrueNASDefaults(config) }
func normalizeVMwareDefaults(config *AlertConfig) { alertconfig.NormalizeVMwareDefaults(config) }
func normalizeGeneralSettings(config *AlertConfig) { alertconfig.NormalizeGeneralSettings(config) }
func normalizeTimeThresholds(config *AlertConfig) { alertconfig.NormalizeTimeThresholds(config) }
func validateHysteresisThresholds(config *AlertConfig) {
alertconfig.ValidateHysteresisThresholds(config)
}
func validateQuietHoursTimezone(config *AlertConfig) { alertconfig.ValidateQuietHoursTimezone(config) }
-10
View File
@@ -1066,16 +1066,6 @@ type OverridePolicy struct {
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
}
func (p OverridePolicy) Validate() error {
if err := p.Target.Validate(); err != nil {
return err
}
if p.ExpiresAt != nil && p.ExpiresAt.IsZero() {
return fmt.Errorf("expires at must be set when provided")
}
return nil
}
func isKnownResourceType(rt unifiedresources.ResourceType) bool {
if rt == "" {
return false
-36
View File
@@ -109,16 +109,6 @@ func unifiedAlertType(typeKey string) string {
}
}
// isUnifiedGuestType returns true for resource types that support I/O metrics.
func isUnifiedGuestType(typeKey string) bool {
switch typeKey {
case "vm", "system-container", "app-container":
return true
default:
return false
}
}
func supportsUnifiedIOMetrics(typeKey string) bool {
switch typeKey {
case "vm", "system-container", "app-container", "k8s-cluster", "k8s-deployment", "pod", "truenas-system", "vmware-host", "vmware-vm":
@@ -597,32 +587,6 @@ func unifiedResourceAlertInstance(resource unifiedresources.Resource, typeKey st
return ""
}
func mergeMetricOptions(base *metricOptions, extra map[string]interface{}) *metricOptions {
if len(extra) == 0 {
return base
}
merged := &metricOptions{}
if base != nil {
*merged = *base
}
if len(extra) > 0 {
if merged.Metadata == nil {
merged.Metadata = make(map[string]interface{}, len(extra))
} else {
copied := make(map[string]interface{}, len(merged.Metadata)+len(extra))
for k, v := range merged.Metadata {
copied[k] = v
}
merged.Metadata = copied
}
for k, v := range extra {
merged.Metadata[k] = v
}
}
return merged
}
func (i *UnifiedResourceInput) CPUValue() float64 {
if i == nil || i.CPU == nil {
return 0
-45
View File
@@ -6,7 +6,6 @@ import (
"time"
alertspecs "github.com/rcourtman/pulse-go-rewrite/internal/alerts/specs"
"github.com/rcourtman/pulse-go-rewrite/internal/storagehealth"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
)
@@ -261,17 +260,6 @@ func resourceSupportsUnifiedIncidentAlerts(resource unifiedresources.Resource) b
}
}
func alertLevelFromIncidentSeverity(level storagehealth.RiskLevel) (AlertLevel, bool) {
switch level {
case storagehealth.RiskCritical:
return AlertLevelCritical, true
case storagehealth.RiskWarning:
return AlertLevelWarning, true
default:
return "", false
}
}
func alertLevelFromCanonicalSeverity(level alertspecs.AlertSeverity) (AlertLevel, bool) {
switch level {
case alertspecs.AlertSeverityCritical:
@@ -526,39 +514,6 @@ func unifiedIncidentConsumerSummary(storage *unifiedresources.StorageMeta) strin
return unifiedresources.StorageConsumerImpactSummary(storage)
}
func unifiedIncidentDependentConsumerSummary(storage *unifiedresources.StorageMeta) string {
if storage == nil || storage.ConsumerCount <= 0 {
return ""
}
names := make([]string, 0, len(storage.TopConsumers))
for _, consumer := range storage.TopConsumers {
name := strings.TrimSpace(consumer.Name)
if name == "" {
continue
}
names = append(names, name)
if len(names) == 3 {
break
}
}
resourceLabel := "dependent resource"
if storage.ConsumerCount != 1 {
resourceLabel = "dependent resources"
}
if len(names) == 0 {
return "Affects " + intLabel(storage.ConsumerCount) + " " + resourceLabel
}
if remaining := storage.ConsumerCount - len(names); remaining > 0 {
return "Affects " + intLabel(storage.ConsumerCount) + " " + resourceLabel + ": " + strings.Join(names, ", ") + ", and " + intLabel(remaining) + " more"
}
return "Affects " + intLabel(storage.ConsumerCount) + " " + resourceLabel + ": " + strings.Join(names, ", ")
}
func unifiedIncidentBackupConsumerSummary(storage *unifiedresources.StorageMeta) string {
if storage == nil || storage.ConsumerCount <= 0 {
return ""
-8
View File
@@ -1,7 +1,6 @@
package api
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -384,10 +383,3 @@ func (b *AgentEventBroadcaster) PublishActionCompletedRecord(record unifiedresou
// call sites don't need this — they hold a direct reference — but
// keeping it here makes the dependency explicit.
type agentEventBroadcasterContextKey struct{}
// ContextWithAgentEventBroadcaster attaches a broadcaster to a
// context. Used by integration tests that need to inject a fake
// broadcaster.
func ContextWithAgentEventBroadcaster(ctx context.Context, b *AgentEventBroadcaster) context.Context {
return context.WithValue(ctx, agentEventBroadcasterContextKey{}, b)
}
-8
View File
@@ -19,14 +19,6 @@ func loadHostedAwareAIConfig(hostedMode bool, billingBaseDir, orgID string, pers
return cfg, nil
}
func shouldAutoBootstrapHostedAIConfig(hostedMode bool, persistence *config.ConfigPersistence) bool {
return false
}
func hostedAIAutoBootstrapEligible(state *billingState) bool {
return false
}
func hostedModeEnabledFromEnv() bool {
return os.Getenv("PULSE_HOSTED_MODE") == "true"
}
-4
View File
@@ -584,10 +584,6 @@ type incidentEventView struct {
Details map[string]interface{} `json:"details"`
}
func emptyIncidentEventView() incidentEventView {
return incidentEventView{}.NormalizeCollections()
}
func (v incidentEventView) NormalizeCollections() incidentEventView {
if v.Details == nil {
v.Details = map[string]interface{}{}
@@ -6,7 +6,6 @@ import (
"github.com/rcourtman/pulse-go-rewrite/internal/ai/chat"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -14,8 +13,6 @@ import (
// Mock implementation of chat.StateProvider
type mockChatStateProvider struct{}
func (m *mockChatStateProvider) ReadSnapshot() models.StateSnapshot { return models.StateSnapshot{} }
func TestChatServiceAdapter_CreateSession(t *testing.T) {
// Setup real chat service with minimal config
cfg := chat.Config{
@@ -30,15 +30,6 @@ func healthEntry(lastSuccess *time.Time, errMessage, errCategory string, breaker
}
}
func desiredAgentConfigFingerprint(t *testing.T, commandsEnabled *bool, settings map[string]interface{}) ConnectionFleetConfigFingerprint {
t.Helper()
desired := desiredAgentConfig(t, commandsEnabled, settings)
if desired.Fingerprint == nil {
t.Fatal("expected desired config fingerprint")
}
return *desired.Fingerprint
}
func desiredAgentConfig(t *testing.T, commandsEnabled *bool, settings map[string]interface{}) connectionAgentDesiredConfig {
t.Helper()
metadata, err := remoteconfig.BuildDesiredConfigMetadata(commandsEnabled, settings)
-17
View File
@@ -17,15 +17,6 @@ type aggregatorRuntimeSources struct {
truenasPoller *monitoring.TrueNASPoller
}
// buildAggregatorInputs gathers the same inputs the HTTP connections handler
// uses so the alerts pipeline can derive identical Connection rows without
// going through the HTTP layer. Returning an empty aggregatorInputs when a
// dependency is unavailable keeps the alerts loop a no-op rather than a hard
// failure.
func buildAggregatorInputs(ctx context.Context, cfg *config.Config, persistence *config.ConfigPersistence, monitor *monitoring.Monitor) aggregatorInputs {
return buildAggregatorInputsWithRuntimeSources(ctx, cfg, persistence, monitor, aggregatorRuntimeSources{})
}
func buildAggregatorInputsWithRuntimeSources(
ctx context.Context,
cfg *config.Config,
@@ -137,14 +128,6 @@ func snapshotConnectionsForAlerts(connections []Connection) []alerts.ConnectionS
return out
}
// BuildAlertConnectionSnapshots returns the platform-connection snapshots the
// alerts package consumes for the connection-degraded check. This is the
// monitor-loop counterpart to the HTTP connections handler — it runs the
// same derivation but skips the JSON envelope.
func BuildAlertConnectionSnapshots(ctx context.Context, cfg *config.Config, persistence *config.ConfigPersistence, monitor *monitoring.Monitor) []alerts.ConnectionSnapshot {
return buildAlertConnectionSnapshotsWithRuntimeSources(ctx, cfg, persistence, monitor, aggregatorRuntimeSources{})
}
func buildAlertConnectionSnapshotsWithRuntimeSources(
ctx context.Context,
cfg *config.Config,
-8
View File
@@ -113,14 +113,6 @@ func pbsHandler(body string) http.HandlerFunc {
}
}
func pmgHandler(body string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", "pmg-api-daemon/8.1")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, body)
}
}
func vmwareHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/xml")
-8
View File
@@ -111,14 +111,6 @@ func publicDemoCommercialPolicyForRequest(
return "", false
}
func publicDemoCommercialRouteInventory() []string {
routes := make([]string, 0, len(publicDemoCommercialPolicies))
for _, policy := range publicDemoCommercialPolicies {
routes = append(routes, policy.route)
}
return routes
}
func sanitizeRuntimeCapabilitiesPayloadForPublicDemo(
payload RuntimeCapabilitiesPayload,
) RuntimeCapabilitiesPayload {
-15
View File
@@ -986,18 +986,3 @@ func TestEntitlementHandler_CommercialMigrationDoesNotExposeTrialStartReason(t *
t.Fatalf("trial_eligibility_reason=%q, want empty", payload.TrialEligibilityReason)
}
}
// countProMinusFreeFeatures returns the number of Pro features not included in Free.
func countProMinusFreeFeatures() int {
freeSet := make(map[string]struct{}, len(license.TierFeatures[license.TierFree]))
for _, f := range license.TierFeatures[license.TierFree] {
freeSet[f] = struct{}{}
}
count := 0
for _, f := range license.TierFeatures[license.TierPro] {
if _, ok := freeSet[f]; !ok {
count++
}
}
return count
}
-5
View File
@@ -279,11 +279,6 @@ func writeLicenseRequiredFromLicensing(w http.ResponseWriter, feature, message s
// licenseTierFreeValue is the canonical free-tier constant for use outside the bridge.
const licenseTierFreeValue = pkglicensing.TierFree
// overflowBonusFromLicensing returns the number of bonus host slots granted by the onboarding overflow.
func overflowBonusFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int {
return pkglicensing.OverflowBonus(tier, overflowGrantedAt, now)
}
// overflowDaysRemainingFromLicensing returns the number of days remaining in the overflow window.
func overflowDaysRemainingFromLicensing(tier licenseTier, overflowGrantedAt *int64, now time.Time) int {
return pkglicensing.OverflowDaysRemaining(tier, overflowGrantedAt, now)
-11
View File
@@ -670,17 +670,6 @@ func (h *LicenseHandlers) syncReleaseDemoFixtureRuntime(orgID string, service *l
}
}
func (h *LicenseHandlers) canonicalMonitoredSystemGrandfatherFloor(ctx context.Context) (int, bool) {
usage := h.entitlementUsageSnapshot(ctx)
if !usage.MonitoredSystemsAvailable {
return 0, false
}
if usage.MonitoredSystems < 0 {
return 0, false
}
return int(usage.MonitoredSystems), true
}
func (h *LicenseHandlers) ensureEvaluatorForOrg(orgID string, service *licenseService) error {
if h == nil || service == nil || h.mtPersistence == nil {
return nil
-27
View File
@@ -172,33 +172,6 @@ func RequireMultiTenant(next http.HandlerFunc) http.HandlerFunc {
}
}
// RequireMultiTenantHandler returns middleware for http.Handler.
func RequireMultiTenantHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
orgID := GetOrgID(r.Context())
// Default org is always allowed (backward compatibility)
if orgID == "" || orgID == "default" {
next.ServeHTTP(w, r)
return
}
// Feature flag check - multi-tenant must be explicitly enabled
if !multiTenantEnabled {
writeMultiTenantDisabledError(w)
return
}
// Non-default orgs require multi-tenant license
if !hasMultiTenantFeatureForContext(r.Context()) {
writeMultiTenantRequiredError(w)
return
}
next.ServeHTTP(w, r)
})
}
// writeMultiTenantRequiredError writes a 402 Payment Required response
// indicating that multi-tenant requires an Enterprise license.
func writeMultiTenantRequiredError(w http.ResponseWriter) {
-5
View File
@@ -87,11 +87,6 @@ func NewTenantMiddlewareWithConfig(cfg TenantMiddlewareConfig) *TenantMiddleware
}
}
// SetAuthChecker sets the authorization checker for the middleware.
func (m *TenantMiddleware) SetAuthChecker(checker AuthorizationChecker) {
m.authChecker = checker
}
func (m *TenantMiddleware) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. Extract Org ID
-10
View File
@@ -1,7 +1,6 @@
package api
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@@ -10,7 +9,6 @@ import (
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
pkglicensing "github.com/rcourtman/pulse-go-rewrite/pkg/licensing"
"github.com/stretchr/testify/assert"
)
@@ -22,14 +20,6 @@ type mockLicenseProvider struct {
hasFeatures bool
}
func (p *mockLicenseProvider) Service(ctx context.Context) *pkglicensing.Service {
// In a real scenario, we'd return a mocked service control structure.
// Since license.Service is concrete, we rely on its default state (no features)
// or we'd need a way to inject state.
// For now, testing the negative case (no license) is most important for security.
return pkglicensing.NewService()
}
func TestTenantMiddleware_Enforcement_Permanent(t *testing.T) {
// Cleanup env after test
defer func() {

Some files were not shown because too many files have changed in this diff Show More