364 lines
87 KiB
HTML
364 lines
87 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>/ — graphify tree viewer</title>
|
|
<style>
|
|
body {
|
|
font-family: 'Segoe UI', sans-serif;
|
|
margin: 0;
|
|
padding: 0;
|
|
background: #f9f9f9;
|
|
color: #333;
|
|
}
|
|
h1 {
|
|
margin: 20px 0 0 24px;
|
|
font-size: 2.2rem;
|
|
font-weight: bold;
|
|
color: #1e3a56;
|
|
}
|
|
.controls {
|
|
margin: 20px 0 15px 24px;
|
|
}
|
|
button {
|
|
margin-right: 10px;
|
|
padding: 8px 18px;
|
|
background: #007bff;
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 5px;
|
|
font-size: 0.95rem;
|
|
cursor: pointer;
|
|
transition: background 0.2s ease-in-out;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
button:hover {
|
|
background: #0056b3;
|
|
}
|
|
button:active {
|
|
background: #004085;
|
|
}
|
|
#tree-container {
|
|
width: calc(100vw - 48px); /* Adjust for body margin/padding */
|
|
height: 85vh;
|
|
overflow: auto;
|
|
border-radius: 8px;
|
|
background: #fff;
|
|
margin-left: 24px;
|
|
margin-right: 24px;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
|
border: 1px solid #ddd;
|
|
}
|
|
svg {
|
|
background: #fff;
|
|
border-radius: 8px;
|
|
display: block; /* Important for D3 */
|
|
}
|
|
.node circle {
|
|
stroke-width: 2.5px;
|
|
}
|
|
.node text { /* Base style for the <text> container */
|
|
font: 13px 'Segoe UI', sans-serif;
|
|
paint-order: stroke fill; /* Ensures text is readable over lines */
|
|
stroke: #fff; /* White halo */
|
|
stroke-width: 3px; /* Halo thickness */
|
|
stroke-linejoin: round;
|
|
stroke-opacity: 0.85; /* Halo opacity */
|
|
}
|
|
.link {
|
|
fill: none;
|
|
stroke-opacity: 0.7;
|
|
stroke-width: 2px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>/ — Knowledge Graph</h1>
|
|
<div class="controls">
|
|
<button onclick="expandAll()">Expand All</button>
|
|
<button onclick="collapseAll()">Collapse All</button>
|
|
<button onclick="resetView()">Reset View</button>
|
|
</div>
|
|
<div id="tree-container">
|
|
<svg id="tree-svg" width="6000" height="8000"></svg>
|
|
</div>
|
|
|
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
<script>
|
|
const initialJsonData = {"name":"/","total_count":1156,"children":[{"name":"apps","total_count":114,"children":[{"name":"demos-static","total_count":50,"children":[{"name":"public","total_count":50,"children":[{"name":"architecture-simulator","total_count":5,"children":[{"name":"architecture-simulator.js","total_count":5,"children":[{"name":"box()","total_count":1,"children":[]},{"name":"canvas","total_count":1,"children":[]},{"name":"ctx","total_count":1,"children":[]},{"name":"draw()","total_count":1,"children":[]},{"name":"state","total_count":1,"children":[]}]}]},{"name":"dev-toolbelt","total_count":5,"children":[{"name":"dev-toolbelt.js","total_count":5,"children":[{"name":"decodeBase64Url()","total_count":1,"children":[]},{"name":"input","total_count":1,"children":[]},{"name":"logFilter","total_count":1,"children":[]},{"name":"output","total_count":1,"children":[]},{"name":"setOutput()","total_count":1,"children":[]}]}]},{"name":"media-cruncher","total_count":14,"children":[{"name":"media-cruncher.js","total_count":14,"children":[{"name":"appendStatus()","total_count":1,"children":[]},{"name":"createCanvas()","total_count":1,"children":[]},{"name":"crunchImage()","total_count":1,"children":[]},{"name":"dropZone","total_count":1,"children":[]},{"name":"extensionByType","total_count":1,"children":[]},{"name":"formatBytes()","total_count":1,"children":[]},{"name":"handleFiles()","total_count":1,"children":[]},{"name":"maxEdge","total_count":1,"children":[]},{"name":"mediaInput","total_count":1,"children":[]},{"name":"outputFormat","total_count":1,"children":[]},{"name":"quality","total_count":1,"children":[]},{"name":"qualityOutput","total_count":1,"children":[]},{"name":"results","total_count":1,"children":[]},{"name":"toBlob()","total_count":1,"children":[]}]}]},{"name":"model-drift","total_count":4,"children":[{"name":"model-drift.js","total_count":4,"children":[{"name":"accuracy","total_count":1,"children":[]},{"name":"canvas","total_count":1,"children":[]},{"name":"ctx","total_count":1,"children":[]},{"name":"draw()","total_count":1,"children":[]}]}]},{"name":"network-quality","total_count":5,"children":[{"name":"network-quality.js","total_count":5,"children":[{"name":"canvas","total_count":1,"children":[]},{"name":"ctx","total_count":1,"children":[]},{"name":"draw()","total_count":1,"children":[]},{"name":"measure()","total_count":1,"children":[]},{"name":"samples","total_count":1,"children":[]}]}]},{"name":"privacy-redactor","total_count":4,"children":[{"name":"privacy-redactor.js","total_count":4,"children":[{"name":"blurRect()","total_count":1,"children":[]},{"name":"canvas","total_count":1,"children":[]},{"name":"ctx","total_count":1,"children":[]},{"name":"placeholder()","total_count":1,"children":[]}]}]},{"name":"sentiment-sandbox","total_count":5,"children":[{"name":"sentiment-sandbox.js","total_count":5,"children":[{"name":"analyze()","total_count":1,"children":[]},{"name":"input","total_count":1,"children":[]},{"name":"negative","total_count":1,"children":[]},{"name":"output","total_count":1,"children":[]},{"name":"positive","total_count":1,"children":[]}]}]},{"name":"theme.js","total_count":6,"children":[{"name":"getDemoThemeButtons()","total_count":1,"children":[]},{"name":"getStoredDemoTheme()","total_count":1,"children":[]},{"name":"installDemoThemeToolbar()","total_count":1,"children":[]},{"name":"normalizeDemoTheme()","total_count":1,"children":[]},{"name":"setDemoTheme()","total_count":1,"children":[]},{"name":"storeDemoTheme()","total_count":1,"children":[]}]},{"name":"traveler-tools","total_count":2,"children":[{"name":"sw.js","total_count":1,"children":[{"name":"ASSETS","total_count":1,"children":[]}]},{"name":"traveler-tools.js","total_count":1,"children":[{"name":"updateTraveler()","total_count":1,"children":[]}]}]}]}]},{"name":"website","total_count":64,"children":[{"name":"blog.php","total_count":1,"children":[{"name":"renderStackSourceLinks()","total_count":1,"children":[]}]},{"name":"cv-theme.js","total_count":6,"children":[{"name":"cvPortrait","total_count":1,"children":[]},{"name":"getSiteThemeButtons()","total_count":1,"children":[]},{"name":"getStoredSiteTheme()","total_count":1,"children":[]},{"name":"normalizeSiteTheme()","total_count":1,"children":[]},{"name":"setSiteTheme()","total_count":1,"children":[]},{"name":"storeSiteTheme()","total_count":1,"children":[]}]},{"name":"homelab-tree.php","total_count":1,"children":[{"name":"OTHER_PAGES","total_count":1,"children":[]}]},{"name":"ideas_helper.php","total_count":8,"children":[{"name":"visitor_idea_clean()","total_count":1,"children":[]},{"name":"visitor_ideas_append()","total_count":1,"children":[]},{"name":"visitor_ideas_dir()","total_count":1,"children":[]},{"name":"visitor_ideas_ensure_dir()","total_count":1,"children":[]},{"name":"visitor_ideas_file()","total_count":1,"children":[]},{"name":"visitor_ideas_rate_file()","total_count":1,"children":[]},{"name":"visitor_ideas_read()","total_count":1,"children":[]},{"name":"visitor_ideas_recently_submitted()","total_count":1,"children":[]}]},{"name":"lang","total_count":2,"children":[{"name":"en.php","total_count":1,"children":[]},{"name":"nah.php","total_count":1,"children":[]}]},{"name":"lang_helper.php","total_count":4,"children":[{"name":"asset_url()","total_count":1,"children":[]},{"name":"cleanRuntimeTranslation()","total_count":1,"children":[]},{"name":"getLang()","total_count":1,"children":[]},{"name":"loadRuntimeTranslations()","total_count":1,"children":[]}]},{"name":"partials","total_count":4,"children":[{"name":"translation_ui.php","total_count":1,"children":[{"name":"STATIC_LANGS","total_count":1,"children":[]}]},{"name":"footer.php","total_count":1,"children":[]},{"name":"header.php","total_count":1,"children":[]},{"name":"theme_toolbar.php","total_count":1,"children":[]}]},{"name":"render-static-pages.sh","total_count":2,"children":[{"name":"render-static-pages.sh script","total_count":1,"children":[]},{"name":"render_page()","total_count":1,"children":[]}]},{"name":"save_idea.php","total_count":1,"children":[{"name":"idea_redirect()","total_count":1,"children":[]}]},{"name":"save_lang.php","total_count":2,"children":[{"name":"clean_translation_value()","total_count":1,"children":[]},{"name":"translation_response()","total_count":1,"children":[]}]},{"name":"start-website.sh","total_count":1,"children":[{"name":"start-website.sh script","total_count":1,"children":[]}]},{"name":"translate.php","total_count":11,"children":[{"name":"clean_translate_text()","total_count":1,"children":[]},{"name":"elapsed_ms()","total_count":1,"children":[]},{"name":"redis_connect()","total_count":1,"children":[]},{"name":"redis_encode_command()","total_count":1,"children":[]},{"name":"redis_read_line()","total_count":1,"children":[]},{"name":"redis_read_response()","total_count":1,"children":[]},{"name":"translate_response()","total_count":1,"children":[]},{"name":"translation_cache_key()","total_count":1,"children":[]},{"name":"translation_cache_read()","total_count":1,"children":[]},{"name":"translation_cache_write()","total_count":1,"children":[]},{"name":"translation_log()","total_count":1,"children":[]}]},{"name":"translation.js","total_count":15,"children":[{"name":"actionSpan","total_count":1,"children":[]},{"name":"badge","total_count":1,"children":[]},{"name":"browserLang","total_count":1,"children":[]},{"name":"btn","total_count":1,"children":[]},{"name":"collectPageTranslations()","total_count":1,"children":[]},{"name":"detectedSpan","total_count":1,"children":[]},{"name":"doTranslation()","total_count":1,"children":[]},{"name":"hidePrompt()","total_count":1,"children":[]},{"name":"initTranslation()","total_count":1,"children":[]},{"name":"LANG_NAMES","total_count":1,"children":[]},{"name":"prompt","total_count":1,"children":[]},{"name":"saveLang()","total_count":1,"children":[]},{"name":"showBadge()","total_count":1,"children":[]},{"name":"translateBatch()","total_count":1,"children":[]},{"name":"urlLang","total_count":1,"children":[]}]},{"name":"visitor-ideas.js","total_count":2,"children":[{"name":"addIdeaText()","total_count":1,"children":[]},{"name":"renderIdeas()","total_count":1,"children":[]}]},{"name":"cv.php","total_count":1,"children":[]},{"name":"demos.php","total_count":1,"children":[]},{"name":"index.php","total_count":1,"children":[]},{"name":"visitor_ideas.php","total_count":1,"children":[]}]}]},{"name":"bootstrap","total_count":252,"children":[{"name":"apps","total_count":10,"children":[{"name":".terraform.lock.hcl","total_count":1,"children":[{"name":"provider.registry.opentofu.org/hashicorp/kubernetes","total_count":1,"children":[]}]},{"name":"main.tf","total_count":4,"children":[{"name":"kubernetes_manifest.argocd_application","total_count":1,"children":[]},{"name":"local.application_sources","total_count":1,"children":[]},{"name":"provider.kubernetes","total_count":1,"children":[]},{"name":"Terraform module: bootstrap/apps","total_count":1,"children":[]}]},{"name":"variables.tf","total_count":5,"children":[{"name":"var.applications","total_count":1,"children":[]},{"name":"var.argocd_namespace","total_count":1,"children":[]},{"name":"var.gitops_repo_url","total_count":1,"children":[]},{"name":"var.kubeconfig_path","total_count":1,"children":[]},{"name":"var.website_image_ref","total_count":1,"children":[]}]}]},{"name":"cluster","total_count":33,"children":[{"name":".terraform.lock.hcl","total_count":2,"children":[{"name":"provider.registry.opentofu.org/hashicorp/external","total_count":1,"children":[]},{"name":"provider.registry.opentofu.org/hashicorp/null","total_count":1,"children":[]}]},{"name":"main.tf","total_count":11,"children":[{"name":"local.control_plane_node_label_pairs","total_count":1,"children":[]},{"name":"local.node_label_pairs","total_count":1,"children":[]},{"name":"local.worker_node_label_pairs","total_count":1,"children":[]},{"name":"null_resource.kube_system_resource_policy","total_count":1,"children":[]},{"name":"null_resource.kubeadm_control_plane","total_count":1,"children":[]},{"name":"null_resource.kubeadm_worker","total_count":1,"children":[]},{"name":"null_resource.node_labels","total_count":1,"children":[]},{"name":"null_resource.worker_nodes_required","total_count":1,"children":[]},{"name":"output.kubeconfig_path","total_count":1,"children":[]},{"name":"output.pod_network_cidr","total_count":1,"children":[]},{"name":"Terraform module: bootstrap/cluster","total_count":1,"children":[]}]},{"name":"variables.tf","total_count":20,"children":[{"name":"var.allow_empty_worker_nodes","total_count":1,"children":[]},{"name":"var.control_plane_advertise_address","total_count":1,"children":[]},{"name":"var.control_plane_node_labels","total_count":1,"children":[]},{"name":"var.control_plane_node_name","total_count":1,"children":[]},{"name":"var.force_worker_rejoin","total_count":1,"children":[]},{"name":"var.kubeconfig_owner","total_count":1,"children":[]},{"name":"var.kubeconfig_path","total_count":1,"children":[]},{"name":"var.node_dns_servers","total_count":1,"children":[]},{"name":"var.persistent_volume_dirs","total_count":1,"children":[]},{"name":"var.pod_network_cidr","total_count":1,"children":[]},{"name":"var.registry_endpoint","total_count":1,"children":[]},{"name":"var.tailscale_nodeport_access","total_count":1,"children":[]},{"name":"var.tailscale_nodeport_extra_ports","total_count":1,"children":[]},{"name":"var.tailscale_nodeport_extra_target_ports","total_count":1,"children":[]},{"name":"var.tailscale_subnet_routes","total_count":1,"children":[]},{"name":"var.worker_node_labels","total_count":1,"children":[]},{"name":"var.worker_nodes","total_count":1,"children":[]},{"name":"var.worker_tailscale_accept_routes","total_count":1,"children":[]},{"name":"var.worker_tailscale_enabled","total_count":1,"children":[]},{"name":"var.worker_tailscale_pod_egress_snat","total_count":1,"children":[]}]}]},{"name":"edge","total_count":43,"children":[{"name":".terraform.lock.hcl","total_count":1,"children":[{"name":"provider.registry.opentofu.org/hashicorp/null","total_count":1,"children":[]}]},{"name":"main.tf","total_count":15,"children":[{"name":"local.compose_file","total_count":1,"children":[]},{"name":"local.config_hash","total_count":1,"children":[]},{"name":"local.default_conf","total_count":1,"children":[]},{"name":"local.default_conf_matrix","total_count":1,"children":[]},{"name":"local.default_vcl","total_count":1,"children":[]},{"name":"local.haproxy_cfg","total_count":1,"children":[]},{"name":"local.heimdall_backend_host","total_count":1,"children":[]},{"name":"local.heimdall_server_name","total_count":1,"children":[]},{"name":"local.main_server_names","total_count":1,"children":[]},{"name":"local.server_names","total_count":1,"children":[]},{"name":"local.squid_conf","total_count":1,"children":[]},{"name":"null_resource.edge_services","total_count":1,"children":[]},{"name":"output.edge_host","total_count":1,"children":[]},{"name":"output.edge_install_dir","total_count":1,"children":[]},{"name":"Terraform module: bootstrap/edge","total_count":1,"children":[]}]},{"name":"variables.tf","total_count":27,"children":[{"name":"var.additional_server_names","total_count":1,"children":[]},{"name":"var.backend_host","total_count":1,"children":[]},{"name":"var.backend_port","total_count":1,"children":[]},{"name":"var.demos_backend_port","total_count":1,"children":[]},{"name":"var.edge_host","total_count":1,"children":[]},{"name":"var.edge_install_dir","total_count":1,"children":[]},{"name":"var.edge_install_docker","total_count":1,"children":[]},{"name":"var.edge_ssh_key_path","total_count":1,"children":[]},{"name":"var.edge_user","total_count":1,"children":[]},{"name":"var.element_backend_host","total_count":1,"children":[]},{"name":"var.element_backend_port","total_count":1,"children":[]},{"name":"var.element_server_name","total_count":1,"children":[]},{"name":"var.enable_letsencrypt","total_count":1,"children":[]},{"name":"var.gitea_backend_host","total_count":1,"children":[]},{"name":"var.gitea_backend_port","total_count":1,"children":[]},{"name":"var.haproxy_stats_password","total_count":1,"children":[]},{"name":"var.haproxy_stats_user","total_count":1,"children":[]},{"name":"var.heimdall_backend_host","total_count":1,"children":[]},{"name":"var.heimdall_backend_port","total_count":1,"children":[]},{"name":"var.letsencrypt_email","total_count":1,"children":[]},{"name":"var.letsencrypt_staging","total_count":1,"children":[]},{"name":"var.matrix_backend_host","total_count":1,"children":[]},{"name":"var.matrix_backend_port","total_count":1,"children":[]},{"name":"var.matrix_cert_dir","total_count":1,"children":[]},{"name":"var.matrix_enabled","total_count":1,"children":[]},{"name":"var.matrix_server_name","total_count":1,"children":[]},{"name":"var.server_name","total_count":1,"children":[]}]}]},{"name":"platform","total_count":77,"children":[{"name":".terraform.lock.hcl","total_count":3,"children":[{"name":"provider.registry.opentofu.org/hashicorp/helm","total_count":1,"children":[]},{"name":"provider.registry.opentofu.org/hashicorp/kubernetes","total_count":1,"children":[]},{"name":"provider.registry.opentofu.org/hashicorp/null","total_count":1,"children":[]}]},{"name":"main.tf","total_count":57,"children":[{"name":"helm_release.argocd","total_count":1,"children":[]},{"name":"helm_release.calico","total_count":1,"children":[]},{"name":"helm_release.calico_crds","total_count":1,"children":[]},{"name":"helm_release.extra_tools","total_count":1,"children":[]},{"name":"helm_release.kyverno","total_count":1,"children":[]},{"name":"helm_release.kyverno_policies","total_count":1,"children":[]},{"name":"helm_release.loki","total_count":1,"children":[]},{"name":"helm_release.metallb","total_count":1,"children":[]},{"name":"helm_release.metrics_server","total_count":1,"children":[]},{"name":"helm_release.mimir","total_count":1,"children":[]},{"name":"helm_release.openebs","total_count":1,"children":[]},{"name":"helm_release.prometheus_stack","total_count":1,"children":[]},{"name":"helm_release.promtail","total_count":1,"children":[]},{"name":"helm_release.traefik","total_count":1,"children":[]},{"name":"kubernetes_config_map.grafana_dashboards","total_count":1,"children":[]},{"name":"kubernetes_manifest.nodelocal_dns_config_map","total_count":1,"children":[]},{"name":"kubernetes_manifest.nodelocal_dns_daemonset","total_count":1,"children":[]},{"name":"kubernetes_manifest.nodelocal_dns_metrics_service","total_count":1,"children":[]},{"name":"kubernetes_manifest.nodelocal_dns_service_account","total_count":1,"children":[]},{"name":"kubernetes_manifest.nodelocal_dns_upstream_service","total_count":1,"children":[]},{"name":"kubernetes_namespace_v1.monitoring","total_count":1,"children":[]},{"name":"kubernetes_storage_class_v1.openebs_hostpath_retain","total_count":1,"children":[]},{"name":"local.argocd_component_affinity","total_count":1,"children":[]},{"name":"local.argocd_component_label_values","total_count":1,"children":[]},{"name":"local.argocd_component_match_labels","total_count":1,"children":[]},{"name":"local.argocd_component_topology_spread_constraints","total_count":1,"children":[]},{"name":"local.argocd_node_selector","total_count":1,"children":[]},{"name":"local.grafana_dashboard_files","total_count":1,"children":[]},{"name":"local.kube_state_metrics_affinity","total_count":1,"children":[]},{"name":"local.kube_state_metrics_match_labels","total_count":1,"children":[]},{"name":"local.kube_state_metrics_topology_spread_constraints","total_count":1,"children":[]},{"name":"local.kyverno_component_label_values","total_count":1,"children":[]},{"name":"local.kyverno_component_match_labels","total_count":1,"children":[]},{"name":"local.kyverno_component_pod_anti_affinity","total_count":1,"children":[]},{"name":"local.kyverno_component_topology_spread_constraints","total_count":1,"children":[]},{"name":"local.kyverno_node_selector","total_count":1,"children":[]},{"name":"local.metallb_ip_address_pool_manifest","total_count":1,"children":[]},{"name":"local.metallb_l2_advertisement_manifest","total_count":1,"children":[]},{"name":"local.metallb_l2_manifests","total_count":1,"children":[]},{"name":"local.nodelocal_dns_corefile","total_count":1,"children":[]},{"name":"local.platform_topology_key","total_count":1,"children":[]},{"name":"local.prometheus_operator_affinity","total_count":1,"children":[]},{"name":"local.prometheus_operator_match_labels","total_count":1,"children":[]},{"name":"local.prometheus_operator_topology_spread_constraints","total_count":1,"children":[]},{"name":"local.prometheus_stack_node_selector","total_count":1,"children":[]},{"name":"local.traefik_node_selector","total_count":1,"children":[]},{"name":"null_resource.argocd_private_repo","total_count":1,"children":[]},{"name":"null_resource.argocd_ready","total_count":1,"children":[]},{"name":"null_resource.calico_helm_recovery","total_count":1,"children":[]},{"name":"null_resource.calico_ready","total_count":1,"children":[]},{"name":"null_resource.metallb_l2_config","total_count":1,"children":[]},{"name":"null_resource.metrics_server_ready","total_count":1,"children":[]},{"name":"null_resource.prometheus_stack_helm_recovery","total_count":1,"children":[]},{"name":"null_resource.prometheus_stack_readiness","total_count":1,"children":[]},{"name":"provider.helm","total_count":1,"children":[]},{"name":"provider.kubernetes","total_count":1,"children":[]},{"name":"Terraform module: bootstrap/platform","total_count":1,"children":[]}]},{"name":"variables.tf","total_count":17,"children":[{"name":"var.argocd","total_count":1,"children":[]},{"name":"var.calico","total_count":1,"children":[]},{"name":"var.calico_node_address_autodetection_cidrs","total_count":1,"children":[]},{"name":"var.calico_operator_node_name","total_count":1,"children":[]},{"name":"var.extra_helm_releases","total_count":1,"children":[]},{"name":"var.gitops_repo_url","total_count":1,"children":[]},{"name":"var.gitops_ssh_key_path","total_count":1,"children":[]},{"name":"var.kubeconfig_path","total_count":1,"children":[]},{"name":"var.kyverno","total_count":1,"children":[]},{"name":"var.metallb","total_count":1,"children":[]},{"name":"var.metrics_server","total_count":1,"children":[]},{"name":"var.nodelocal_dns","total_count":1,"children":[]},{"name":"var.observability","total_count":1,"children":[]},{"name":"var.openebs","total_count":1,"children":[]},{"name":"var.pod_network_cidr","total_count":1,"children":[]},{"name":"var.prometheus_stack_node_selector","total_count":1,"children":[]},{"name":"var.traefik","total_count":1,"children":[]}]}]},{"name":"provisioning","total_count":89,"children":[{"name":".terraform.lock.hcl","total_count":1,"children":[{"name":"provider.registry.opentofu.org/hashicorp/null","total_count":1,"children":[]}]},{"name":"main.tf","total_count":28,"children":[{"name":"local.config_hash","total_count":1,"children":[]},{"name":"local.debian_netboot_base_url","total_count":1,"children":[]},{"name":"local.dnsmasq_conf","total_count":1,"children":[]},{"name":"local.golden_node_prepare","total_count":1,"children":[]},{"name":"local.grub_cfg","total_count":1,"children":[]},{"name":"local.http_root","total_count":1,"children":[]},{"name":"local.kernel_cgroup_boot_options","total_count":1,"children":[]},{"name":"local.nginx_conf","total_count":1,"children":[]},{"name":"local.node_dns_servers","total_count":1,"children":[]},{"name":"local.pimox_template_net0","total_count":1,"children":[]},{"name":"local.prepare_template","total_count":1,"children":[]},{"name":"local.prepare_template_script_url","total_count":1,"children":[]},{"name":"local.preseed_cfg","total_count":1,"children":[]},{"name":"local.preseed_url","total_count":1,"children":[]},{"name":"local.provisioning_http_base_url","total_count":1,"children":[]},{"name":"local.provisioning_script_url","total_count":1,"children":[]},{"name":"local.repo_root","total_count":1,"children":[]},{"name":"local.rescue_index","total_count":1,"children":[]},{"name":"local.ssh_authorized_keys_base64","total_count":1,"children":[]},{"name":"local.template_package_list","total_count":1,"children":[]},{"name":"local.template_packages","total_count":1,"children":[]},{"name":"local.template_user_ssh_keys","total_count":1,"children":[]},{"name":"local.tftp_root","total_count":1,"children":[]},{"name":"null_resource.pimox_template_vm_create","total_count":1,"children":[]},{"name":"null_resource.pimox_template_vm_finalize","total_count":1,"children":[]},{"name":"null_resource.pimox_template_vm_seal","total_count":1,"children":[]},{"name":"null_resource.provisioning_host","total_count":1,"children":[]},{"name":"Terraform module: bootstrap/provisioning","total_count":1,"children":[]}]},{"name":"outputs.tf","total_count":6,"children":[{"name":"output.pimox_template_vmid","total_count":1,"children":[]},{"name":"output.preseed_url","total_count":1,"children":[]},{"name":"output.provisioning_http_base_url","total_count":1,"children":[]},{"name":"output.pxe_boot_file","total_count":1,"children":[]},{"name":"output.pxe_rescue_url","total_count":1,"children":[]},{"name":"output.tftp_root","total_count":1,"children":[]}]},{"name":"variables.tf","total_count":54,"children":[{"name":"var.additional_template_packages","total_count":1,"children":[]},{"name":"var.clone_hostname_prefix","total_count":1,"children":[]},{"name":"var.debian_mirror_directory","total_count":1,"children":[]},{"name":"var.debian_mirror_host","total_count":1,"children":[]},{"name":"var.debian_netboot_base_url","total_count":1,"children":[]},{"name":"var.debian_suite","total_count":1,"children":[]},{"name":"var.http_host","total_count":1,"children":[]},{"name":"var.http_port","total_count":1,"children":[]},{"name":"var.kernel_cgroup_boot_options","total_count":1,"children":[]},{"name":"var.keyboard","total_count":1,"children":[]},{"name":"var.kubernetes_minor_version","total_count":1,"children":[]},{"name":"var.locale","total_count":1,"children":[]},{"name":"var.node_dns_servers","total_count":1,"children":[]},{"name":"var.pimox_host","total_count":1,"children":[]},{"name":"var.pimox_qm_bin","total_count":1,"children":[]},{"name":"var.pimox_ssh_key_path","total_count":1,"children":[]},{"name":"var.pimox_template_bridge","total_count":1,"children":[]},{"name":"var.pimox_template_build_host","total_count":1,"children":[]},{"name":"var.pimox_template_build_ssh_key_path","total_count":1,"children":[]},{"name":"var.pimox_template_build_timeout","total_count":1,"children":[]},{"name":"var.pimox_template_build_timeout_seconds","total_count":1,"children":[]},{"name":"var.pimox_template_build_user","total_count":1,"children":[]},{"name":"var.pimox_template_builder_enabled","total_count":1,"children":[]},{"name":"var.pimox_template_cores","total_count":1,"children":[]},{"name":"var.pimox_template_efidisk0","total_count":1,"children":[]},{"name":"var.pimox_template_guest_ip_prefix","total_count":1,"children":[]},{"name":"var.pimox_template_guest_ssh_timeout_seconds","total_count":1,"children":[]},{"name":"var.pimox_template_kvm_cpuset","total_count":1,"children":[]},{"name":"var.pimox_template_mac","total_count":1,"children":[]},{"name":"var.pimox_template_memory","total_count":1,"children":[]},{"name":"var.pimox_template_name","total_count":1,"children":[]},{"name":"var.pimox_template_replace_existing","total_count":1,"children":[]},{"name":"var.pimox_template_scsi0","total_count":1,"children":[]},{"name":"var.pimox_template_vmid","total_count":1,"children":[]},{"name":"var.pimox_user","total_count":1,"children":[]},{"name":"var.provisioning_host","total_count":1,"children":[]},{"name":"var.provisioning_install_dir","total_count":1,"children":[]},{"name":"var.provisioning_interface","total_count":1,"children":[]},{"name":"var.provisioning_ssh_key_path","total_count":1,"children":[]},{"name":"var.provisioning_user","total_count":1,"children":[]},{"name":"var.proxy_dhcp_range","total_count":1,"children":[]},{"name":"var.pxe_boot_file","total_count":1,"children":[]},{"name":"var.pxe_default_menu_entry","total_count":1,"children":[]},{"name":"var.pxe_menu_timeout","total_count":1,"children":[]},{"name":"var.registry_endpoint","total_count":1,"children":[]},{"name":"var.template_disk","total_count":1,"children":[]},{"name":"var.template_domain","total_count":1,"children":[]},{"name":"var.template_hostname","total_count":1,"children":[]},{"name":"var.template_user","total_count":1,"children":[]},{"name":"var.template_user_full_name","total_count":1,"children":[]},{"name":"var.template_user_password_hash","total_count":1,"children":[]},{"name":"var.template_user_ssh_authorized_keys","total_count":1,"children":[]},{"name":"var.template_user_ssh_public_key_path","total_count":1,"children":[]},{"name":"var.timezone","total_count":1,"children":[]}]}]}]},{"name":"cmd","total_count":25,"children":[{"name":"jeannie-core","total_count":25,"children":[{"name":"main.go","total_count":21,"children":[{"name":"dagSpec","total_count":1,"children":[]},{"name":"dagTask","total_count":1,"children":[]},{"name":"dagTaskResult","total_count":1,"children":[]},{"name":"envDefault()","total_count":1,"children":[]},{"name":"executeDAG()","total_count":1,"children":[]},{"name":"filterReportRows()","total_count":1,"children":[]},{"name":"includeReportRow()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"normalizeStatus()","total_count":1,"children":[]},{"name":"orderedReportAreas()","total_count":1,"children":[]},{"name":"parseReportRows()","total_count":1,"children":[]},{"name":"renderReportText()","total_count":1,"children":[]},{"name":"reportRow","total_count":1,"children":[]},{"name":"reportStatusRank()","total_count":1,"children":[]},{"name":"rowsForReportArea()","total_count":1,"children":[]},{"name":"runDAG()","total_count":1,"children":[]},{"name":"runDAGTask()","total_count":1,"children":[]},{"name":"runReportRender()","total_count":1,"children":[]},{"name":"truncate()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]},{"name":"writeDAGLine()","total_count":1,"children":[]}]},{"name":"main_test.go","total_count":4,"children":[{"name":"TestExecuteDAGRespectsDependencies()","total_count":1,"children":[]},{"name":"TestExecuteDAGReturnsFailedTask()","total_count":1,"children":[]},{"name":"TestReportRenderHidesOKGraphAndQuery()","total_count":1,"children":[]},{"name":"TestReportRenderProblemsOnly()","total_count":1,"children":[]}]}]}]},{"name":"go.mod","total_count":1,"children":[{"name":"my-homelab-configs","total_count":1,"children":[]}]},{"name":"infra","total_count":51,"children":[{"name":"arr-stack","total_count":12,"children":[{"name":"bootstrap.sh","total_count":1,"children":[{"name":"bootstrap.sh script","total_count":1,"children":[]}]},{"name":"configure-arr.py","total_count":11,"children":[{"name":"apply_import_lists()","total_count":1,"children":[]},{"name":"Configure safe baseline ARR app settings from local environment values.","total_count":1,"children":[]},{"name":"ensure_qbittorrent()","total_count":1,"children":[]},{"name":"ensure_root_folder()","total_count":1,"children":[]},{"name":"fill_fields()","total_count":1,"children":[]},{"name":"load_env()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"request_json()","total_count":1,"children":[]},{"name":"required_env()","total_count":1,"children":[]},{"name":"schema_fields()","total_count":1,"children":[]},{"name":"wait_for_app()","total_count":1,"children":[]}]}]},{"name":"blockchain-devnet","total_count":6,"children":[{"name":"metrics","total_count":6,"children":[{"name":"exporter.py","total_count":6,"children":[{"name":".do_GET()","total_count":1,"children":[]},{"name":".log_message()","total_count":1,"children":[]},{"name":"collect_metrics()","total_count":1,"children":[]},{"name":"hex_to_int()","total_count":1,"children":[]},{"name":"MetricsHandler","total_count":1,"children":[]},{"name":"rpc()","total_count":1,"children":[]}]}]}]},{"name":"heimdall","total_count":10,"children":[{"name":"seed-heimdall.py","total_count":10,"children":[{"name":"columns()","total_count":1,"children":[]},{"name":"ensure_dashboard_tag()","total_count":1,"children":[]},{"name":"ensure_public_dashboard()","total_count":1,"children":[]},{"name":"find_database()","total_count":1,"children":[]},{"name":"home_dashboard_tag_id()","total_count":1,"children":[]},{"name":"load_links()","total_count":1,"children":[]},{"name":"quote_identifier()","total_count":1,"children":[]},{"name":"table_exists()","total_count":1,"children":[]},{"name":"upsert_links()","total_count":1,"children":[]},{"name":"wait_for_items_table()","total_count":1,"children":[]}]}]},{"name":"matrix-server","total_count":2,"children":[{"name":"bootstrap.sh","total_count":2,"children":[{"name":"bootstrap.sh script","total_count":1,"children":[]},{"name":"render_homeserver()","total_count":1,"children":[]}]}]},{"name":"omniroute","total_count":1,"children":[{"name":"bootstrap.sh","total_count":1,"children":[{"name":"bootstrap.sh script","total_count":1,"children":[]}]}]},{"name":"rpi-services","total_count":19,"children":[{"name":"bootstrap.sh","total_count":10,"children":[{"name":"bootstrap.sh script","total_count":1,"children":[]},{"name":"configure_docker_root()","total_count":1,"children":[]},{"name":"docker_root_target()","total_count":1,"children":[]},{"name":"install_missing_packages()","total_count":1,"children":[]},{"name":"resolve_pihole_webpassword()","total_count":1,"children":[]},{"name":"seed_uptime_kuma_monitors()","total_count":1,"children":[]},{"name":"stop_legacy_pihole()","total_count":1,"children":[]},{"name":"sync_pihole_adlists()","total_count":1,"children":[]},{"name":"sync_pihole_managed_dns()","total_count":1,"children":[]},{"name":"write_env_file()","total_count":1,"children":[]}]},{"name":"seed-uptime-kuma.js","total_count":9,"children":[{"name":"defaultForColumn()","total_count":1,"children":[]},{"name":"findModuleDirectory()","total_count":1,"children":[]},{"name":"fs","total_count":1,"children":[]},{"name":"loadDatabaseModule()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"monitorValues()","total_count":1,"children":[]},{"name":"nowSql()","total_count":1,"children":[]},{"name":"path","total_count":1,"children":[]},{"name":"upsertMonitor()","total_count":1,"children":[]}]}]},{"name":"youtube-backup","total_count":1,"children":[{"name":"backup-youtube.sh","total_count":1,"children":[{"name":"backup-youtube.sh script","total_count":1,"children":[]}]}]}]},{"name":"jeannie","total_count":303,"children":[{"name":"(+103 more)","total_count":103,"children":[]},{"name":"access_audit()","total_count":1,"children":[]},{"name":"adopt_apps_existing_resources()","total_count":1,"children":[]},{"name":"adopt_platform_existing_resources()","total_count":1,"children":[]},{"name":"adopt_tofu_helm_release()","total_count":1,"children":[]},{"name":"adopt_tofu_kubernetes_manifest()","total_count":1,"children":[]},{"name":"adopt_tofu_kubernetes_resource()","total_count":1,"children":[]},{"name":"agent_sandbox()","total_count":1,"children":[]},{"name":"ai_check()","total_count":1,"children":[]},{"name":"ai_evals()","total_count":1,"children":[]},{"name":"ai_index()","total_count":1,"children":[]},{"name":"ai_memory()","total_count":1,"children":[]},{"name":"ai_scheduler()","total_count":1,"children":[]},{"name":"apps()","total_count":1,"children":[]},{"name":"apps_registry_endpoint()","total_count":1,"children":[]},{"name":"artifact_cache()","total_count":1,"children":[]},{"name":"ask_homelab()","total_count":1,"children":[]},{"name":"backstage_brain_enabled()","total_count":1,"children":[]},{"name":"backstage_brain_note()","total_count":1,"children":[]},{"name":"backup_gitea()","total_count":1,"children":[]},{"name":"backup_status()","total_count":1,"children":[]},{"name":"backup_tofu_state()","total_count":1,"children":[]},{"name":"blockchain_devnet()","total_count":1,"children":[]},{"name":"blockchain_test()","total_count":1,"children":[]},{"name":"blockchain_wallet()","total_count":1,"children":[]},{"name":"bootstrap_gitea_repo()","total_count":1,"children":[]},{"name":"capacity_advisor()","total_count":1,"children":[]},{"name":"capacity_limits()","total_count":1,"children":[]},{"name":"capacity_report()","total_count":1,"children":[]},{"name":"cert_check()","total_count":1,"children":[]},{"name":"change_journal()","total_count":1,"children":[]},{"name":"check_debian_docker_root()","total_count":1,"children":[]},{"name":"check_debian_tailscale_ip()","total_count":1,"children":[]},{"name":"check_edge_disk_free()","total_count":1,"children":[]},{"name":"check_edge_gitea_backend()","total_count":1,"children":[]},{"name":"check_edge_heimdall_backend()","total_count":1,"children":[]},{"name":"check_edge_ssh()","total_count":1,"children":[]},{"name":"check_edge_traefik_backend()","total_count":1,"children":[]},{"name":"check_gitea_git_remote()","total_count":1,"children":[]},{"name":"check_gitea_reachable()","total_count":1,"children":[]},{"name":"check_gitea_ssh()","total_count":1,"children":[]},{"name":"check_local_disk_free_gib()","total_count":1,"children":[]},{"name":"check_pihole_dns_query()","total_count":1,"children":[]},{"name":"check_pimox_storage()","total_count":1,"children":[]},{"name":"check_pimox_worker_tailnet_egress()","total_count":1,"children":[]},{"name":"check_rpi_docker_root_state()","total_count":1,"children":[]},{"name":"check_rpi_docker_writable()","total_count":1,"children":[]},{"name":"check_rpi_ssh()","total_count":1,"children":[]},{"name":"check_rpi_tailscale_ip()","total_count":1,"children":[]},{"name":"check_systemd_active()","total_count":1,"children":[]},{"name":"cleanup_calico_links()","total_count":1,"children":[]},{"name":"cleanup_calico_runtime_files()","total_count":1,"children":[]},{"name":"cleanup_iptables()","total_count":1,"children":[]},{"name":"cleanup_mounts()","total_count":1,"children":[]},{"name":"cleanup_node()","total_count":1,"children":[]},{"name":"cluster_admin_kubeconfig_present()","total_count":1,"children":[]},{"name":"cluster_control_plane_tracked()","total_count":1,"children":[]},{"name":"cluster_worker_targets()","total_count":1,"children":[]},{"name":"cluster_worker_var_file_has_workers()","total_count":1,"children":[]},{"name":"configure_pimox_worker_forwarding()","total_count":1,"children":[]},{"name":"configure_pimox_worker_guest_network()","total_count":1,"children":[]},{"name":"control_plane()","total_count":1,"children":[]},{"name":"cosign_registry_flags()","total_count":1,"children":[]},{"name":"cpuset_cpu_count()","total_count":1,"children":[]},{"name":"create_gitea_repo()","total_count":1,"children":[]},{"name":"create_gitea_repo_deploy_key()","total_count":1,"children":[]},{"name":"delete_prometheus_stack_storage()","total_count":1,"children":[]},{"name":"demos_image_is_current()","total_count":1,"children":[]},{"name":"demos_registry_endpoint()","total_count":1,"children":[]},{"name":"demos_source_hash()","total_count":1,"children":[]},{"name":"deploy_gitea()","total_count":1,"children":[]},{"name":"deploy_heimdall()","total_count":1,"children":[]},{"name":"deploy_hermes()","total_count":1,"children":[]},{"name":"deploy_matrix()","total_count":1,"children":[]},{"name":"deploy_omniroute()","total_count":1,"children":[]},{"name":"deploy_rpi_services()","total_count":1,"children":[]},{"name":"destroy_pimox_worker_vms()","total_count":1,"children":[]},{"name":"detect_route_interface()","total_count":1,"children":[]},{"name":"disabled_value()","total_count":1,"children":[]},{"name":"docker_root_dir()","total_count":1,"children":[]},{"name":"doctor_cluster()","total_count":1,"children":[]},{"name":"doctor_edge()","total_count":1,"children":[]},{"name":"doctor_gitea()","total_count":1,"children":[]},{"name":"doctor_preapply()","total_count":1,"children":[]},{"name":"doctor_rpi()","total_count":1,"children":[]},{"name":"doctor_versions()","total_count":1,"children":[]},{"name":"doctor_versions_compact()","total_count":1,"children":[]},{"name":"doctor_versions_report()","total_count":1,"children":[]},{"name":"drill_gitea_restore()","total_count":1,"children":[]},{"name":"drill_pihole_restore()","total_count":1,"children":[]},{"name":"drill_restore()","total_count":1,"children":[]},{"name":"dump_argocd_debug()","total_count":1,"children":[]},{"name":"dump_namespace_debug()","total_count":1,"children":[]},{"name":"edge_apply()","total_count":1,"children":[]},{"name":"edge_haproxy_stats_credentials_file()","total_count":1,"children":[]},{"name":"edge_haproxy_stats_password_valid()","total_count":1,"children":[]},{"name":"ensure_cluster_worker_var_file()","total_count":1,"children":[]},{"name":"ensure_cosign_available()","total_count":1,"children":[]},{"name":"ensure_cosign_keypair()","total_count":1,"children":[]},{"name":"ensure_cosign_password()","total_count":1,"children":[]},{"name":"ensure_docker_build_space()","total_count":1,"children":[]},{"name":"ensure_edge_haproxy_stats_password()","total_count":1,"children":[]},{"name":"ensure_edge_tailscale_routes()","total_count":1,"children":[]},{"name":"ensure_existing_cluster_started_for_up()","total_count":1,"children":[]},{"name":"ensure_gitea_repo_ssh_access()","total_count":1,"children":[]},{"name":"ensure_go_toolchain()","total_count":1,"children":[]},{"name":"ensure_homelab_node_labels()","total_count":1,"children":[]},{"name":"ensure_kubernetes_api_ready_for_tofu_stack()","total_count":1,"children":[]},{"name":"ensure_local_docker_compose()","total_count":1,"children":[]},{"name":"ensure_pimox_worker_node()","total_count":1,"children":[]},{"name":"ensure_python3()","total_count":1,"children":[]},{"name":"ensure_sops_age_tools()","total_count":1,"children":[]},{"name":"ensure_static_pimox_worker_spec_file()","total_count":1,"children":[]},{"name":"ensure_website_image_tag_not_reused()","total_count":1,"children":[]},{"name":"explain()","total_count":1,"children":[]},{"name":"export_homelab_inventory_tf_vars()","total_count":1,"children":[]},{"name":"export_if_unset()","total_count":1,"children":[]},{"name":"fix_debian_docker_root()","total_count":1,"children":[]},{"name":"generate_edge_haproxy_stats_password()","total_count":1,"children":[]},{"name":"gitea_api_base_url()","total_count":1,"children":[]},{"name":"gitea_bootstrap_password()","total_count":1,"children":[]},{"name":"gitea_branch_exists()","total_count":1,"children":[]},{"name":"gitea_public_key_registered()","total_count":1,"children":[]},{"name":"gitea_repo_exists()","total_count":1,"children":[]},{"name":"gitops_status()","total_count":1,"children":[]},{"name":"golden_ledger()","total_count":1,"children":[]},{"name":"grafana_dashboards()","total_count":1,"children":[]},{"name":"heal()","total_count":1,"children":[]},{"name":"helm_release_secret_exists()","total_count":1,"children":[]},{"name":"homelab_map()","total_count":1,"children":[]},{"name":"homelab_preflight()","total_count":1,"children":[]},{"name":"image_digest_ref()","total_count":1,"children":[]},{"name":"image_ref_tag()","total_count":1,"children":[]},{"name":"image_state_value()","total_count":1,"children":[]},{"name":"image_supply_chain_metadata_exists()","total_count":1,"children":[]},{"name":"impact()","total_count":1,"children":[]},{"name":"incident_commander()","total_count":1,"children":[]},{"name":"install_gitea_backup_timer()","total_count":1,"children":[]},{"name":"install_gitea_runner()","total_count":1,"children":[]},{"name":"install_missing_debian_packages()","total_count":1,"children":[]},{"name":"inventory_check()","total_count":1,"children":[]},{"name":"jeannie script","total_count":1,"children":[]},{"name":"jeannie_compact_text()","total_count":1,"children":[]},{"name":"jeannie_log_start()","total_count":1,"children":[]},{"name":"jeannie_step_latest_progress_line()","total_count":1,"children":[]},{"name":"jeannie_step_plan()","total_count":1,"children":[]},{"name":"jeannie_step_prefix()","total_count":1,"children":[]},{"name":"jeannie_step_progress()","total_count":1,"children":[]},{"name":"jeannie_verbose_enabled()","total_count":1,"children":[]},{"name":"kubeconfig_readonly()","total_count":1,"children":[]},{"name":"kubernetes_api_reachable()","total_count":1,"children":[]},{"name":"kubernetes_resource_exists()","total_count":1,"children":[]},{"name":"load_homelab_inventory_defaults()","total_count":1,"children":[]},{"name":"model_observe()","total_count":1,"children":[]},{"name":"move_prometheus_stack_workers()","total_count":1,"children":[]},{"name":"nuke()","total_count":1,"children":[]},{"name":"nuke_for_rebuild()","total_count":1,"children":[]},{"name":"ollama_install_from_tgz()","total_count":1,"children":[]},{"name":"ollama_linux_arch()","total_count":1,"children":[]},{"name":"ollama_setup()","total_count":1,"children":[]},{"name":"ollama_write_systemd_service()","total_count":1,"children":[]},{"name":"openwrt()","total_count":1,"children":[]},{"name":"path_available_mb()","total_count":1,"children":[]},{"name":"pimox_generated_mac()","total_count":1,"children":[]},{"name":"pimox_guest_cpu_count()","total_count":1,"children":[]},{"name":"pimox_guest_exec_exitcode()","total_count":1,"children":[]},{"name":"pimox_guest_ipv4()","total_count":1,"children":[]},{"name":"pimox_shutdown_vm_gracefully()","total_count":1,"children":[]},{"name":"pimox_ssh()","total_count":1,"children":[]},{"name":"pimox_start_vm_with_cpuset()","total_count":1,"children":[]},{"name":"pimox_worker_count_default()","total_count":1,"children":[]},{"name":"pimox_worker_count_effective()","total_count":1,"children":[]},{"name":"pimox_worker_cpu_affinity()","total_count":1,"children":[]},{"name":"pimox_worker_guest_agent_recovery_hint()","total_count":1,"children":[]},{"name":"pimox_worker_guest_ssh_repair()","total_count":1,"children":[]},{"name":"pimox_worker_net0_config()","total_count":1,"children":[]},{"name":"pimox_worker_static_ip()","total_count":1,"children":[]},{"name":"pimox_worker_vm_debug()","total_count":1,"children":[]},{"name":"plan_homelab()","total_count":1,"children":[]},{"name":"platform_apply()","total_count":1,"children":[]},{"name":"preflight_check()","total_count":1,"children":[]},{"name":"preflight_warn()","total_count":1,"children":[]},{"name":"prepare_buildx_builder()","total_count":1,"children":[]},{"name":"prepare_cluster_worker_var_file()","total_count":1,"children":[]},{"name":"print_usage()","total_count":1,"children":[]},{"name":"promote()","total_count":1,"children":[]},{"name":"prompt_injection_lab()","total_count":1,"children":[]},{"name":"prune_unused_docker_build_data()","total_count":1,"children":[]},{"name":"publish_cosign_public_key_config_map()","total_count":1,"children":[]},{"name":"publish_image_supply_chain_metadata()","total_count":1,"children":[]},{"name":"rebuild_cluster()","total_count":1,"children":[]},{"name":"record_change_journal()","total_count":1,"children":[]},{"name":"recover_edge_haproxy_stats_password()","total_count":1,"children":[]},{"name":"recover_plan()","total_count":1,"children":[]},{"name":"recover_power()","total_count":1,"children":[]},{"name":"recreate_pods_for_selector()","total_count":1,"children":[]},{"name":"red_blue_loop()","total_count":1,"children":[]},{"name":"redact_sensitive_output()","total_count":1,"children":[]},{"name":"refresh_argocd_application()","total_count":1,"children":[]},{"name":"registry_image_exists()","total_count":1,"children":[]},{"name":"release_snapshot()","total_count":1,"children":[]}]},{"name":"labs","total_count":12,"children":[{"name":"mlops-platform","total_count":12,"children":[{"name":"app","total_count":11,"children":[{"name":"main.py","total_count":11,"children":[{"name":"drift_score()","total_count":1,"children":[]},{"name":"healthz()","total_count":1,"children":[]},{"name":"load_model()","total_count":1,"children":[]},{"name":"logistic()","total_count":1,"children":[]},{"name":"metrics()","total_count":1,"children":[]},{"name":"normalized_features()","total_count":1,"children":[]},{"name":"predict()","total_count":1,"children":[]},{"name":"PredictRequest","total_count":1,"children":[]},{"name":"record_http_metrics()","total_count":1,"children":[]},{"name":"root()","total_count":1,"children":[]},{"name":"score_prediction()","total_count":1,"children":[]}]}]},{"name":"training","total_count":1,"children":[{"name":"export_model.py","total_count":1,"children":[{"name":"export_model()","total_count":1,"children":[]}]}]}]}]},{"name":"renovate.json","total_count":9,"children":[{"name":"$schema","total_count":1,"children":[]},{"name":"config:recommended","total_count":1,"children":[]},{"name":"customManagers","total_count":1,"children":[]},{"name":"dependencyDashboard","total_count":1,"children":[]},{"name":"extends","total_count":1,"children":[]},{"name":"labels","total_count":1,"children":[]},{"name":"packageRules","total_count":1,"children":[]},{"name":"prConcurrentLimit","total_count":1,"children":[]},{"name":"timezone","total_count":1,"children":[]}]},{"name":"scripts","total_count":362,"children":[{"name":"access-audit","total_count":9,"children":[{"name":"access-audit script","total_count":1,"children":[]},{"name":"git_access()","total_count":1,"children":[]},{"name":"kubernetes_access()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"runner_access()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"ssh_inventory()","total_count":1,"children":[]},{"name":"tailnet_access()","total_count":1,"children":[]},{"name":"warn_file_mode()","total_count":1,"children":[]}]},{"name":"agent-sandbox","total_count":5,"children":[{"name":"Classify proposed agent commands against the homelab sandbox policy.","total_count":1,"children":[]},{"name":"classify()","total_count":1,"children":[]},{"name":"load_policy()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"print_result()","total_count":1,"children":[]}]},{"name":"ai-evals","total_count":12,"children":[{"name":"Case","total_count":1,"children":[]},{"name":"Deterministic eval harness for Jeannie and homelab RAG behavior.","total_count":1,"children":[]},{"name":"fixture_text()","total_count":1,"children":[]},{"name":"load_cases()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"print_list()","total_count":1,"children":[]},{"name":"print_run()","total_count":1,"children":[]},{"name":"print_show()","total_count":1,"children":[]},{"name":"run_case()","total_count":1,"children":[]},{"name":"run_explain_output()","total_count":1,"children":[]},{"name":"run_repo_scan()","total_count":1,"children":[]},{"name":"split_terms()","total_count":1,"children":[]}]},{"name":"ai-memory","total_count":3,"children":[{"name":"Check local homelab AI memory provenance and freshness.","total_count":1,"children":[]},{"name":"check()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]}]},{"name":"ai-scheduler","total_count":7,"children":[{"name":"as_bool()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"read_tsv()","total_count":1,"children":[]},{"name":"recommend()","total_count":1,"children":[]},{"name":"score_node()","total_count":1,"children":[]},{"name":"Simulate placement for AI-ish homelab workloads.","total_count":1,"children":[]},{"name":"simulate()","total_count":1,"children":[]}]},{"name":"artifact-cache","total_count":1,"children":[{"name":"artifact-cache script","total_count":1,"children":[]}]},{"name":"ask","total_count":2,"children":[{"name":"ask script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"backup-status","total_count":5,"children":[{"name":"backup-status script","total_count":1,"children":[]},{"name":"check_latest()","total_count":1,"children":[]},{"name":"file_age_hours()","total_count":1,"children":[]},{"name":"latest_file()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]}]},{"name":"blockchain-devnet","total_count":3,"children":[{"name":"blockchain-devnet script","total_count":1,"children":[]},{"name":"rpc_call()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"blockchain-test","total_count":2,"children":[{"name":"blockchain-test script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"blockchain-wallet","total_count":4,"children":[{"name":"blockchain-wallet script","total_count":1,"children":[]},{"name":"require_private_key()","total_count":1,"children":[]},{"name":"run_cast()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"build-homelab-ai-index","total_count":10,"children":[{"name":"Build a small local retrieval index from non-secret homelab repo files.","total_count":1,"children":[]},{"name":"build_index()","total_count":1,"children":[]},{"name":"candidate_files()","total_count":1,"children":[]},{"name":"chunk_file()","total_count":1,"children":[]},{"name":"load_patterns()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"path_is_secret()","total_count":1,"children":[]},{"name":"path_matches_any()","total_count":1,"children":[]},{"name":"read_text()","total_count":1,"children":[]},{"name":"tokenise()","total_count":1,"children":[]}]},{"name":"capacity-advisor","total_count":2,"children":[{"name":"capacity-advisor script","total_count":1,"children":[]},{"name":"run_check()","total_count":1,"children":[]}]},{"name":"capacity-limits","total_count":3,"children":[{"name":"capacity-limits script","total_count":1,"children":[]},{"name":"cleanup()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"capacity-report","total_count":17,"children":[{"name":"capacity-report script","total_count":1,"children":[]},{"name":"compact_guidance()","total_count":1,"children":[]},{"name":"compact_host_capacity()","total_count":1,"children":[]},{"name":"compact_kubernetes_capacity()","total_count":1,"children":[]},{"name":"compact_main()","total_count":1,"children":[]},{"name":"compact_pimox_capacity()","total_count":1,"children":[]},{"name":"compact_rpi_capacity()","total_count":1,"children":[]},{"name":"emit()","total_count":1,"children":[]},{"name":"grafana_explore_url()","total_count":1,"children":[]},{"name":"host_capacity()","total_count":1,"children":[]},{"name":"kubernetes_capacity()","total_count":1,"children":[]},{"name":"pimox_capacity()","total_count":1,"children":[]},{"name":"rpi_capacity()","total_count":1,"children":[]},{"name":"run_optional()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"summary()","total_count":1,"children":[]},{"name":"verbose_main()","total_count":1,"children":[]}]},{"name":"cert-check","total_count":7,"children":[{"name":"cert-check script","total_count":1,"children":[]},{"name":"cleanup()","total_count":1,"children":[]},{"name":"have()","total_count":1,"children":[]},{"name":"inventory_hosts()","total_count":1,"children":[]},{"name":"inventory_scalar()","total_count":1,"children":[]},{"name":"mark_fail()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]}]},{"name":"change-journal","total_count":4,"children":[{"name":"append_entry()","total_count":1,"children":[]},{"name":"change-journal script","total_count":1,"children":[]},{"name":"latest_entries()","total_count":1,"children":[]},{"name":"timestamp()","total_count":1,"children":[]}]},{"name":"control-plane","total_count":5,"children":[{"name":"control-plane script","total_count":1,"children":[]},{"name":"require_kubectl()","total_count":1,"children":[]},{"name":"show_non_system_pods()","total_count":1,"children":[]},{"name":"show_taints()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"explain","total_count":8,"children":[{"name":"cleanup()","total_count":1,"children":[]},{"name":"explain script","total_count":1,"children":[]},{"name":"explain_output()","total_count":1,"children":[]},{"name":"is_read_only_command()","total_count":1,"children":[]},{"name":"is_read_only_subcommand()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"run_target()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"gitops-status","total_count":1,"children":[{"name":"gitops-status script","total_count":1,"children":[]}]},{"name":"golden-ledger","total_count":1,"children":[{"name":"golden-ledger script","total_count":1,"children":[]}]},{"name":"grafana-dashboards","total_count":4,"children":[{"name":"apply_dashboards()","total_count":1,"children":[]},{"name":"grafana-dashboards script","total_count":1,"children":[]},{"name":"list_dashboards()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"heal","total_count":22,"children":[{"name":"apply_plan()","total_count":1,"children":[]},{"name":"build_plan()","total_count":1,"children":[]},{"name":"clear_failed_action()","total_count":1,"children":[]},{"name":"command_for()","total_count":1,"children":[]},{"name":"HealRule","total_count":1,"children":[]},{"name":"highest_impact()","total_count":1,"children":[]},{"name":"impact_sort_key()","total_count":1,"children":[]},{"name":"load_heal_state()","total_count":1,"children":[]},{"name":"load_status()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"pimox_worker_index()","total_count":1,"children":[]},{"name":"pimox_worker_ready()","total_count":1,"children":[]},{"name":"Plan and apply guarded self-healing actions from Jeannie status findings.","total_count":1,"children":[]},{"name":"print_ai_context()","total_count":1,"children":[]},{"name":"print_plan()","total_count":1,"children":[]},{"name":"recent_failed_commands()","total_count":1,"children":[]},{"name":"record_failed_action()","total_count":1,"children":[]},{"name":"rule_for()","total_count":1,"children":[]},{"name":"status_rows()","total_count":1,"children":[]},{"name":"target_is_healed()","total_count":1,"children":[]},{"name":"verify_healed()","total_count":1,"children":[]},{"name":"write_heal_state()","total_count":1,"children":[]}]},{"name":"homelab-map","total_count":2,"children":[{"name":"homelab-map script","total_count":1,"children":[]},{"name":"inventory()","total_count":1,"children":[]}]},{"name":"impact","total_count":2,"children":[{"name":"impact script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"incident-commander","total_count":14,"children":[{"name":"classify()","total_count":1,"children":[]},{"name":"first_evidence_lines()","total_count":1,"children":[]},{"name":"fixture_text()","total_count":1,"children":[]},{"name":"Incident","total_count":1,"children":[]},{"name":"incident_for_class()","total_count":1,"children":[]},{"name":"load_incidents()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"print_list()","total_count":1,"children":[]},{"name":"print_triage()","total_count":1,"children":[]},{"name":"Read-only incident classification and triage for homelab failures.","total_count":1,"children":[]},{"name":"read_triage_input()","total_count":1,"children":[]},{"name":"render_triage()","total_count":1,"children":[]},{"name":"replay()","total_count":1,"children":[]},{"name":"split_items()","total_count":1,"children":[]}]},{"name":"jeannie-core","total_count":2,"children":[{"name":"core_sources_newer()","total_count":1,"children":[]},{"name":"jeannie-core script","total_count":1,"children":[]}]},{"name":"kubeconfig-readonly","total_count":4,"children":[{"name":"kubeconfig-readonly script","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"require_kubectl()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"model-observe","total_count":10,"children":[{"name":"ask_ollama()","total_count":1,"children":[]},{"name":"grade()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"offline_answer()","total_count":1,"children":[]},{"name":"print_prompts()","total_count":1,"children":[]},{"name":"prompts()","total_count":1,"children":[]},{"name":"report()","total_count":1,"children":[]},{"name":"run()","total_count":1,"children":[]},{"name":"split_terms()","total_count":1,"children":[]},{"name":"Track local model behavior with stable homelab prompts.","total_count":1,"children":[]}]},{"name":"promote","total_count":6,"children":[{"name":"checks()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"Plan and validate canary promotion gates.","total_count":1,"children":[]},{"name":"plan()","total_count":1,"children":[]},{"name":"rollback()","total_count":1,"children":[]},{"name":"validate()","total_count":1,"children":[]}]},{"name":"prompt-injection-lab","total_count":11,"children":[{"name":"Case","total_count":1,"children":[]},{"name":"defense_response()","total_count":1,"children":[]},{"name":"fixture_text()","total_count":1,"children":[]},{"name":"load_cases()","total_count":1,"children":[]},{"name":"Local prompt-injection defense drills for homelab AI workflows.","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"print_list()","total_count":1,"children":[]},{"name":"print_run()","total_count":1,"children":[]},{"name":"print_show()","total_count":1,"children":[]},{"name":"run_case()","total_count":1,"children":[]},{"name":"split_terms()","total_count":1,"children":[]}]},{"name":"query-homelab-ai-index","total_count":8,"children":[{"name":"ask_ollama()","total_count":1,"children":[]},{"name":"load_index()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"Query the local homelab retrieval index and optionally ask Ollama.","total_count":1,"children":[]},{"name":"render_citations()","total_count":1,"children":[]},{"name":"render_context()","total_count":1,"children":[]},{"name":"score_chunks()","total_count":1,"children":[]},{"name":"tokenise()","total_count":1,"children":[]}]},{"name":"recover-plan","total_count":5,"children":[{"name":"check()","total_count":1,"children":[]},{"name":"has_gitea_backup()","total_count":1,"children":[]},{"name":"has_state_backup()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"recover-plan script","total_count":1,"children":[]}]},{"name":"recover-power","total_count":3,"children":[{"name":"recover-power script","total_count":1,"children":[]},{"name":"run_optional()","total_count":1,"children":[]},{"name":"run_step()","total_count":1,"children":[]}]},{"name":"red-blue-loop","total_count":6,"children":[{"name":"checks()","total_count":1,"children":[]},{"name":"ledger()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"plan()","total_count":1,"children":[]},{"name":"Run or plan the homelab red-team/blue-team loop.","total_count":1,"children":[]},{"name":"run()","total_count":1,"children":[]}]},{"name":"release-snapshot","total_count":5,"children":[{"name":"have()","total_count":1,"children":[]},{"name":"latest_file()","total_count":1,"children":[]},{"name":"release-snapshot script","total_count":1,"children":[]},{"name":"run_optional()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]}]},{"name":"render-docs","total_count":5,"children":[{"name":"main()","total_count":1,"children":[]},{"name":"read_inventory_value()","total_count":1,"children":[]},{"name":"render-docs script","total_count":1,"children":[]},{"name":"render_template()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"render-service-catalog","total_count":15,"children":[{"name":"check_file()","total_count":1,"children":[]},{"name":"host_ip()","total_count":1,"children":[]},{"name":"host_label()","total_count":1,"children":[]},{"name":"host_rows()","total_count":1,"children":[]},{"name":"html_link()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"markdown_table()","total_count":1,"children":[]},{"name":"md_link()","total_count":1,"children":[]},{"name":"parse_inventory()","total_count":1,"children":[]},{"name":"Render homelab service catalog artifacts from homelab.yml.","total_count":1,"children":[]},{"name":"render_html()","total_count":1,"children":[]},{"name":"render_markdown()","total_count":1,"children":[]},{"name":"service_rows()","total_count":1,"children":[]},{"name":"value()","total_count":1,"children":[]},{"name":"write_if_changed()","total_count":1,"children":[]}]},{"name":"report-render","total_count":2,"children":[{"name":"report-render script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"report-ui","total_count":11,"children":[{"name":"report-ui script","total_count":1,"children":[]},{"name":"report_ui_begin()","total_count":1,"children":[]},{"name":"report_ui_check()","total_count":1,"children":[]},{"name":"report_ui_cleanup()","total_count":1,"children":[]},{"name":"report_ui_emit()","total_count":1,"children":[]},{"name":"report_ui_first_line()","total_count":1,"children":[]},{"name":"report_ui_one_line()","total_count":1,"children":[]},{"name":"report_ui_parse_args()","total_count":1,"children":[]},{"name":"report_ui_render()","total_count":1,"children":[]},{"name":"report_ui_trim_detail()","total_count":1,"children":[]},{"name":"report_ui_usage_flags()","total_count":1,"children":[]}]},{"name":"resource-budget","total_count":8,"children":[{"name":"fail()","total_count":1,"children":[]},{"name":"have()","total_count":1,"children":[]},{"name":"host_budget()","total_count":1,"children":[]},{"name":"kubernetes_budget()","total_count":1,"children":[]},{"name":"resource-budget script","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]},{"name":"warn()","total_count":1,"children":[]}]},{"name":"restore-drill","total_count":10,"children":[{"name":"check_file()","total_count":1,"children":[]},{"name":"drill_gitea()","total_count":1,"children":[]},{"name":"drill_gitops()","total_count":1,"children":[]},{"name":"drill_pihole()","total_count":1,"children":[]},{"name":"drill_state()","total_count":1,"children":[]},{"name":"latest_file()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"restore-drill script","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"review-last-change","total_count":2,"children":[{"name":"review-last-change script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"route-inventory","total_count":3,"children":[{"name":"cleanup()","total_count":1,"children":[]},{"name":"route-inventory script","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]}]},{"name":"safety-case","total_count":8,"children":[{"name":"changed_files()","total_count":1,"children":[]},{"name":"Generate a read-only safety case for homelab changes.","total_count":1,"children":[]},{"name":"git_lines()","total_count":1,"children":[]},{"name":"load_rules()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"matched_rules()","total_count":1,"children":[]},{"name":"print_safety_case()","total_count":1,"children":[]},{"name":"risk_order()","total_count":1,"children":[]}]},{"name":"scorecard","total_count":5,"children":[{"name":"docs_fresh()","total_count":1,"children":[]},{"name":"inventory_ok()","total_count":1,"children":[]},{"name":"score()","total_count":1,"children":[]},{"name":"scorecard script","total_count":1,"children":[]},{"name":"security_static_ok()","total_count":1,"children":[]}]},{"name":"security-attack-path","total_count":7,"children":[{"name":"jsonpath_or_unknown()","total_count":1,"children":[]},{"name":"kubectl_ro()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"require_kubectl()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"security-attack-path script","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"security-logs","total_count":7,"children":[{"name":"collect_gitea_logs()","total_count":1,"children":[]},{"name":"collect_traefik_logs()","total_count":1,"children":[]},{"name":"ensure_report_dir()","total_count":1,"children":[]},{"name":"main()","total_count":1,"children":[]},{"name":"print_matches()","total_count":1,"children":[]},{"name":"security-logs script","total_count":1,"children":[]},{"name":"timestamp()","total_count":1,"children":[]}]},{"name":"security-scan","total_count":21,"children":[{"name":"ensure_report_dir()","total_count":1,"children":[]},{"name":"header_value()","total_count":1,"children":[]},{"name":"lynis_findings()","total_count":1,"children":[]},{"name":"prepare_security_scans()","total_count":1,"children":[]},{"name":"print_lynis_fallback_focus()","total_count":1,"children":[]},{"name":"print_lynis_focus()","total_count":1,"children":[]},{"name":"print_lynis_llm_focus()","total_count":1,"children":[]},{"name":"require_docker()","total_count":1,"children":[]},{"name":"run_gitleaks()","total_count":1,"children":[]},{"name":"run_kube_bench()","total_count":1,"children":[]},{"name":"run_lynis_host()","total_count":1,"children":[]},{"name":"run_nuclei()","total_count":1,"children":[]},{"name":"run_security_web()","total_count":1,"children":[]},{"name":"run_trivy()","total_count":1,"children":[]},{"name":"run_zap()","total_count":1,"children":[]},{"name":"safe_target_name()","total_count":1,"children":[]},{"name":"scan_targets()","total_count":1,"children":[]},{"name":"security-scan script","total_count":1,"children":[]},{"name":"timestamp()","total_count":1,"children":[]},{"name":"truthy()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]}]},{"name":"synthetic-checks","total_count":7,"children":[{"name":"check()","total_count":1,"children":[]},{"name":"dns_query()","total_count":1,"children":[]},{"name":"http_ok()","total_count":1,"children":[]},{"name":"kubectl_ok()","total_count":1,"children":[]},{"name":"section()","total_count":1,"children":[]},{"name":"synthetic-checks script","total_count":1,"children":[]},{"name":"warn_check()","total_count":1,"children":[]}]},{"name":"validate-homelab","total_count":11,"children":[{"name":"main()","total_count":1,"children":[]},{"name":"run_step()","total_count":1,"children":[]},{"name":"shell_files()","total_count":1,"children":[]},{"name":"validate-homelab script","total_count":1,"children":[]},{"name":"validate_bash_syntax()","total_count":1,"children":[]},{"name":"validate_go()","total_count":1,"children":[]},{"name":"validate_security_static()","total_count":1,"children":[]},{"name":"validate_shellcheck()","total_count":1,"children":[]},{"name":"validate_tofu_fmt()","total_count":1,"children":[]},{"name":"validate_unit_tests()","total_count":1,"children":[]},{"name":"validate_yaml()","total_count":1,"children":[]}]},{"name":"validate-homelab-inventory","total_count":1,"children":[{"name":"validate-homelab-inventory script","total_count":1,"children":[]}]},{"name":"validate-tailnet-policy","total_count":5,"children":[{"name":"main()","total_count":1,"children":[]},{"name":"parse_simple_inventory()","total_count":1,"children":[]},{"name":"require()","total_count":1,"children":[]},{"name":"strip_hujson()","total_count":1,"children":[]},{"name":"Validate the repo-managed Tailscale policy without contacting Tailscale.","total_count":1,"children":[]}]},{"name":"workers","total_count":19,"children":[{"name":"drain_node()","total_count":1,"children":[]},{"name":"have()","total_count":1,"children":[]},{"name":"is_skipped()","total_count":1,"children":[]},{"name":"kubectl_cmd()","total_count":1,"children":[]},{"name":"list_workers()","total_count":1,"children":[]},{"name":"node_for_index()","total_count":1,"children":[]},{"name":"pimox_ssh()","total_count":1,"children":[]},{"name":"rebalance()","total_count":1,"children":[]},{"name":"recreate_plan()","total_count":1,"children":[]},{"name":"restart_workers()","total_count":1,"children":[]},{"name":"start_workers()","total_count":1,"children":[]},{"name":"stop_workers()","total_count":1,"children":[]},{"name":"tailnet_setup()","total_count":1,"children":[]},{"name":"target_indexes()","total_count":1,"children":[]},{"name":"uncordon_node()","total_count":1,"children":[]},{"name":"usage()","total_count":1,"children":[]},{"name":"validate_index()","total_count":1,"children":[]},{"name":"vmid_for_index()","total_count":1,"children":[]},{"name":"workers script","total_count":1,"children":[]}]}]},{"name":"tests","total_count":27,"children":[{"name":"jeannie-unit","total_count":27,"children":[{"name":"assert_command_failure_contains()","total_count":1,"children":[]},{"name":"assert_command_success()","total_count":1,"children":[]},{"name":"assert_contains()","total_count":1,"children":[]},{"name":"assert_eq()","total_count":1,"children":[]},{"name":"cleanup()","total_count":1,"children":[]},{"name":"fail()","total_count":1,"children":[]},{"name":"jeannie-unit script","total_count":1,"children":[]},{"name":"JEANNIE_LIBRARY_MODE","total_count":1,"children":[]},{"name":"pass()","total_count":1,"children":[]},{"name":"PATH","total_count":1,"children":[]},{"name":"test_cluster_worker_targets_include_pimox_and_manual_workers()","total_count":1,"children":[]},{"name":"test_configured_worker_count_can_expand_target_set()","total_count":1,"children":[]},{"name":"test_configured_worker_count_cannot_hide_existing_workers()","total_count":1,"children":[]},{"name":"test_heal_cooldown_escalates_to_diagnostics()","total_count":1,"children":[]},{"name":"test_inventory_validator_accepts_current_inventory()","total_count":1,"children":[]},{"name":"test_inventory_validator_rejects_ip_drift()","total_count":1,"children":[]},{"name":"test_pimox_guest_exec_exitcode_parser()","total_count":1,"children":[]},{"name":"test_pimox_worker_count_detects_generated_topology()","total_count":1,"children":[]},{"name":"test_pimox_worker_net0_config_defaults_to_deterministic_mac()","total_count":1,"children":[]},{"name":"test_pimox_worker_static_ip_selection()","total_count":1,"children":[]},{"name":"test_prepare_cluster_worker_var_file_can_include_raspberry_worker()","total_count":1,"children":[]},{"name":"test_prepare_cluster_worker_var_file_excludes_manual_by_default()","total_count":1,"children":[]},{"name":"test_prepare_cluster_worker_var_file_merges_manual_and_pimox_workers()","total_count":1,"children":[]},{"name":"test_prepare_cluster_worker_var_file_rebuilds_static_pimox_spec()","total_count":1,"children":[]},{"name":"test_report_renderer_empty_problems_message()","total_count":1,"children":[]},{"name":"test_report_renderer_hides_ok_links()","total_count":1,"children":[]},{"name":"test_report_renderer_problems_only_hides_ok_rows()","total_count":1,"children":[]}]}]}]};
|
|
|
|
function transformData(jsonData) {
|
|
// Helper function to recursively build the children structure
|
|
function processNode(node, parentL1StageName) {
|
|
let displayName = node.name;
|
|
// Append total_count if it exists and is not already in the name
|
|
if (node.total_count !== undefined) {
|
|
if (!/\(Total Count: \d+\)$/.test(displayName)) {
|
|
displayName += ` (Total Count: ${node.total_count})`;
|
|
}
|
|
}
|
|
|
|
const newNode = { name: displayName };
|
|
|
|
if (parentL1StageName === "Root") {
|
|
newNode.originalStageName = node.name;
|
|
} else {
|
|
newNode.originalStageName = parentL1StageName;
|
|
}
|
|
|
|
if (node.children && node.children.length > 0) {
|
|
const stageNameToPass = (parentL1StageName === "Root") ? node.name : parentL1StageName;
|
|
newNode.children = node.children.map(child => processNode(child, stageNameToPass));
|
|
}
|
|
|
|
return newNode;
|
|
}
|
|
|
|
let rootDisplayName = jsonData.name;
|
|
if (jsonData.total_count !== undefined && !/\(Total Count: \d+\)$/.test(rootDisplayName)) {
|
|
rootDisplayName += ` (Total Count: ${jsonData.total_count})`;
|
|
}
|
|
|
|
return {
|
|
name: rootDisplayName,
|
|
originalStageName: "Root",
|
|
children: (jsonData.children || []).map(child => processNode(child, "Root"))
|
|
};
|
|
}
|
|
|
|
const treeData = transformData(initialJsonData);
|
|
|
|
// Auto-populated phaseColors: every depth-1 child of the root gets
|
|
// a stable colour from a bigger palette so all top-level dirs are
|
|
// distinguishable.
|
|
const PALETTE = [
|
|
["#3498DB","#2980B9","#AED6F1"], ["#2ECC71","#27AE60","#A9DFBF"],
|
|
["#E74C3C","#C0392B","#F5B7B1"], ["#9B59B6","#8E44AD","#D7BDE2"],
|
|
["#F39C12","#D68910","#FAD7A0"], ["#1ABC9C","#117864","#A2D9CE"],
|
|
["#34495E","#1B2631","#ABB2B9"], ["#E67E22","#BA4A00","#F5CBA7"],
|
|
["#16A085","#0E6655","#A2D9CE"], ["#D35400","#A04000","#EDBB99"],
|
|
["#7F8C8D","#566573","#D5DBDB"], ["#C0392B","#7B241C","#F5B7B1"],
|
|
["#2E86C1","#1B4F72","#A9CCE3"], ["#28B463","#196F3D","#A9DFBF"],
|
|
["#AF7AC5","#6C3483","#D2B4DE"],
|
|
];
|
|
const phaseColors = { "Root": { fill: "#4A4A4A", stroke: "#333333", collapsedFill: "#6C757D" },
|
|
"Default": { fill: "#BDC3C7", stroke: "#95A5A6", collapsedFill: "#ECF0F1" } };
|
|
(initialJsonData.children || []).forEach((c, i) => {
|
|
const pal = PALETTE[i % PALETTE.length];
|
|
phaseColors[c.name] = { fill: pal[0], stroke: pal[1], collapsedFill: pal[2] };
|
|
});
|
|
|
|
const levelSpecificPalettes = {
|
|
0: { fill: "#4A4A4A", stroke: "#333333", collapsedFill: "#6C757D" },
|
|
2: { fill: "#6ab04c", stroke: "#508a38", collapsedFill: "#a3d391" },
|
|
3: { fill: "#f0932b", stroke: "#d0730f", collapsedFill: "#f6c07e" },
|
|
4: { fill: "#be2edd", stroke: "#a01cb3", collapsedFill: "#e08bf2" },
|
|
5: { fill: "#00a8ff", stroke: "#007ac1", collapsedFill: "#74d2ff" },
|
|
6: { fill: "#e55039", stroke: "#c23620", collapsedFill: "#f09a8d" },
|
|
default: { fill: "#747d8c", stroke: "#57606f", collapsedFill: "#a4b0be" }
|
|
};
|
|
|
|
const svgElement = d3.select("#tree-svg");
|
|
const initialSvgWidth = +svgElement.attr("width");
|
|
const initialSvgHeight = +svgElement.attr("height");
|
|
const margin = { top: 40, right: 120, bottom: 80, left: 450 };
|
|
let width = initialSvgWidth - margin.left - margin.right;
|
|
let height = initialSvgHeight - margin.top - margin.bottom;
|
|
const duration = 500;
|
|
let nodeCounter = 0;
|
|
const g = svgElement.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
|
|
const treemap = d3.tree().nodeSize([40, 0]);
|
|
let rootNode = d3.hierarchy(treeData, d => d.children);
|
|
rootNode.x0 = 0;
|
|
rootNode.y0 = 0;
|
|
|
|
if (rootNode.children) {
|
|
rootNode.children.forEach(d_child => {
|
|
if (d_child.children) { collapseBranch(d_child); }
|
|
});
|
|
}
|
|
updateTree(rootNode);
|
|
|
|
function collapseBranch(d) { if (d.children) { d._children = d.children; d._children.forEach(collapseBranch); d.children = null; } }
|
|
function expandBranch(d) { if (d._children) { d.children = d._children; d._children = null; } if (d.children) { d.children.forEach(expandBranch); } }
|
|
window.expandAll = () => { expandBranch(rootNode); updateTree(rootNode); };
|
|
window.collapseAll = () => { if (rootNode.children) { rootNode.children.forEach(collapseBranch); } updateTree(rootNode); };
|
|
window.resetView = () => { if (rootNode.children) { rootNode.children.forEach(d_child => { if (d_child.children || d_child._children) { collapseBranch(d_child); } }); } if (rootNode._children && !rootNode.children) { rootNode.children = rootNode._children; rootNode._children = null; } updateTree(rootNode); };
|
|
|
|
function updateTree(source) {
|
|
const treeLayoutData = treemap(rootNode);
|
|
let nodes = treeLayoutData.descendants();
|
|
let links = treeLayoutData.descendants().slice(1);
|
|
|
|
let minX = 0;
|
|
let maxX = 0;
|
|
if (nodes.length > 0) {
|
|
minX = d3.min(nodes, d => d.x);
|
|
maxX = d3.max(nodes, d => d.x);
|
|
}
|
|
|
|
let neededHeight = Math.max(initialSvgHeight, maxX - minX + margin.top + margin.bottom + 100);
|
|
svgElement.transition().duration(duration / 2).attr("height", neededHeight);
|
|
g.transition().duration(duration / 2).attr("transform", `translate(${margin.left},${margin.top - minX + 40})`);
|
|
|
|
nodes.forEach(d => { d.y = d.depth * 400; }); // Adjust horizontal separation if needed
|
|
|
|
const node = g.selectAll('g.node').data(nodes, d => d.id || (d.id = ++nodeCounter));
|
|
const nodeEnter = node.enter().append('g')
|
|
.attr('class', d => "node" + (d.children || d._children ? " node--internal" : " node--leaf") + (d._children ? " _children" : ""))
|
|
.attr('transform', d => `translate(${source.y0},${source.x0})`)
|
|
.on('click', (event, d) => { if (d.children) { d._children = d.children; d.children = null; } else if (d._children) { d.children = d._children; d._children = null; } updateTree(d); })
|
|
.style('cursor', d => (d.children || d._children) ? 'pointer' : 'default');
|
|
|
|
nodeEnter.append('circle').attr('r', 1e-6);
|
|
|
|
nodeEnter.append('text')
|
|
.attr('dy', '.35em')
|
|
.attr('x', d => d.children || d._children ? -14 : 14)
|
|
.attr('text-anchor', d => d.children || d._children ? 'end' : 'start')
|
|
.style("fill-opacity", 1e-6)
|
|
.call(wrapText, 380);
|
|
|
|
const nodeUpdate = nodeEnter.merge(node);
|
|
nodeUpdate.transition().duration(duration)
|
|
.attr('transform', d => `translate(${d.y},${d.x})`)
|
|
.attr('class', d => "node" + (d.children ? " node--internal" : " node--leaf") + (d._children ? " node--internal _children" : ""));
|
|
|
|
nodeUpdate.select('circle').attr('r', 8.5)
|
|
.style('fill', d => {
|
|
let palette;
|
|
if (d.depth === 0) {
|
|
palette = levelSpecificPalettes[0];
|
|
} else if (d.depth === 1) {
|
|
palette = phaseColors[d.data.originalStageName] || phaseColors.Default;
|
|
} else {
|
|
palette = levelSpecificPalettes[d.depth] || levelSpecificPalettes.default;
|
|
}
|
|
if (d._children) return palette.collapsedFill;
|
|
if (d.children) return palette.fill;
|
|
return "#fff";
|
|
})
|
|
.style('stroke', d => {
|
|
let palette;
|
|
if (d.depth === 0) {
|
|
palette = levelSpecificPalettes[0];
|
|
} else if (d.depth === 1) {
|
|
palette = phaseColors[d.data.originalStageName] || phaseColors.Default;
|
|
} else {
|
|
palette = levelSpecificPalettes[d.depth] || levelSpecificPalettes.default;
|
|
}
|
|
return palette.stroke;
|
|
});
|
|
nodeUpdate.select('text').style("fill-opacity", 1).call(wrapText, 380);
|
|
|
|
const nodeExit = node.exit().transition().duration(duration).attr('transform', d => `translate(${source.y},${source.x})`).remove();
|
|
nodeExit.select('circle').attr('r', 1e-6);
|
|
nodeExit.select('text').style('fill-opacity', 1e-6);
|
|
|
|
const link = g.selectAll('path.link').data(links, d => d.id);
|
|
const linkEnter = link.enter().insert('path', "g").attr('class', 'link').attr('d', d => { const o = { x: source.x0, y: source.y0 }; return diagonal(o, o); });
|
|
|
|
linkEnter.merge(link).transition().duration(duration).attr('d', d => diagonal(d, d.parent))
|
|
.style('stroke', d => {
|
|
const sourceNode = d.parent;
|
|
if (!sourceNode) return phaseColors.Default.stroke;
|
|
const l1AncestorName = sourceNode.data.originalStageName;
|
|
const colorPalette = phaseColors[l1AncestorName] || phaseColors.Default;
|
|
return colorPalette.stroke;
|
|
});
|
|
link.exit().transition().duration(duration).attr('d', d => { const o = { x: source.x, y: source.y }; return diagonal(o, o); }).remove();
|
|
nodes.forEach(d => { d.x0 = d.x; d.y0 = d.y; });
|
|
}
|
|
|
|
function diagonal(s, d) { return `M ${s.y} ${s.x} C ${(s.y + d.y) / 2} ${s.x}, ${(s.y + d.y) / 2} ${d.x}, ${d.y} ${d.x}`; }
|
|
|
|
function wrapText(textElements, maxWidth) {
|
|
const textPartColors = {
|
|
name: '#343a40',
|
|
count: '#0056b3'
|
|
};
|
|
const countRegex = /(\s\(Total Count: \d+\))$/;
|
|
|
|
textElements.each(function () {
|
|
const textD3 = d3.select(this);
|
|
const originalNodeText = textD3.datum().data.name;
|
|
const x = parseFloat(textD3.attr("x") || 0);
|
|
const initialDy = textD3.attr("dy");
|
|
const textAnchor = textD3.attr("text-anchor");
|
|
const lineHeight = 1.1;
|
|
|
|
textD3.text(null);
|
|
|
|
let namePart = originalNodeText;
|
|
let countPartText = "";
|
|
|
|
const countMatch = originalNodeText.match(countRegex);
|
|
if (countMatch && originalNodeText.endsWith(countMatch[0])) {
|
|
namePart = originalNodeText.substring(0, originalNodeText.length - countMatch[0].length).trim();
|
|
countPartText = countMatch[0].trim();
|
|
}
|
|
|
|
const tokens = [];
|
|
namePart.split(/\s+/).filter(Boolean).forEach(word => {
|
|
tokens.push({ text: word, type: 'name' });
|
|
});
|
|
if (countPartText) {
|
|
tokens.push({ text: countPartText, type: 'count' });
|
|
}
|
|
|
|
if (tokens.length === 0 && originalNodeText) {
|
|
tokens.push({ text: originalNodeText, type: 'name' });
|
|
}
|
|
|
|
let currentTspan = textD3.append("tspan").attr("x", x).attr("dy", initialDy);
|
|
if (textAnchor === "end") currentTspan.attr("text-anchor", "end");
|
|
|
|
let lineTokens = [];
|
|
|
|
for (let i = 0; i < tokens.length; i++) {
|
|
const tokenObj = tokens[i];
|
|
|
|
lineTokens.push(tokenObj);
|
|
currentTspan.text(lineTokens.map(t => t.text).join(" "));
|
|
|
|
if (currentTspan.node().getComputedTextLength() > maxWidth && lineTokens.length > 1) {
|
|
lineTokens.pop();
|
|
|
|
currentTspan.text(null);
|
|
lineTokens.forEach((prevToken, idx) => {
|
|
currentTspan.append("tspan")
|
|
.text((idx > 0 ? " " : "") + prevToken.text)
|
|
.style("fill", textPartColors[prevToken.type] || textPartColors.name)
|
|
.style("font-weight", prevToken.type === 'count' ? "bold" : "normal");
|
|
});
|
|
|
|
lineTokens = [tokenObj];
|
|
currentTspan = textD3.append("tspan").attr("x", x).attr("dy", lineHeight + "em");
|
|
if (textAnchor === "end") currentTspan.attr("text-anchor", "end");
|
|
}
|
|
}
|
|
|
|
currentTspan.text(null);
|
|
lineTokens.forEach((token, idx) => {
|
|
currentTspan.append("tspan")
|
|
.text((idx > 0 ? " " : "") + token.text)
|
|
.style("fill", textPartColors[token.type] || textPartColors.name)
|
|
.style("font-weight", token.type === 'count' ? "bold" : "normal");
|
|
});
|
|
|
|
if (textD3.selectAll("tspan > tspan").empty() && textD3.select("tspan").text().length === 0 && originalNodeText) {
|
|
let t = textD3.select("tspan");
|
|
let displayText = originalNodeText;
|
|
t.text(displayText).style("fill", textPartColors.name);
|
|
if (t.node() && t.node().getComputedTextLength() > maxWidth && displayText.length > 20) {
|
|
let estimatedChars = Math.floor(maxWidth / (t.node().getComputedTextLength()/displayText.length) );
|
|
displayText = displayText.substring(0, Math.max(0, estimatedChars - 3)) + "...";
|
|
t.text(displayText);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|