detectkit 0.55.0__tar.gz → 0.55.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. {detectkit-0.55.0/detectkit.egg-info → detectkit-0.55.1}/PKG-INFO +1 -1
  2. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/__init__.py +1 -1
  3. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/assets/ui.js +12 -9
  4. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/server.py +8 -2
  5. {detectkit-0.55.0 → detectkit-0.55.1/detectkit.egg-info}/PKG-INFO +1 -1
  6. {detectkit-0.55.0 → detectkit-0.55.1}/LICENSE +0 -0
  7. {detectkit-0.55.0 → detectkit-0.55.1}/MANIFEST.in +0 -0
  8. {detectkit-0.55.0 → detectkit-0.55.1}/README.md +0 -0
  9. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/__init__.py +0 -0
  10. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/__init__.py +0 -0
  11. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/base.py +0 -0
  12. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/branding.py +0 -0
  13. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/email.py +0 -0
  14. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/factory.py +0 -0
  15. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/mattermost.py +0 -0
  16. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/slack.py +0 -0
  17. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/telegram.py +0 -0
  18. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/channels/webhook.py +0 -0
  19. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/__init__.py +0 -0
  20. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_base.py +0 -0
  21. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
  22. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_decision.py +0 -0
  23. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
  24. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_recovery.py +0 -0
  25. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_replay.py +0 -0
  26. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/_types.py +0 -0
  27. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
  28. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/__init__.py +0 -0
  29. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/_base.py +0 -0
  30. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/_types.py +0 -0
  31. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/autotuner.py +0 -0
  32. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/axis_spec.py +0 -0
  33. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/config_emitter.py +0 -0
  34. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/crossval.py +0 -0
  35. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/detector_select.py +0 -0
  36. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/distribution.py +0 -0
  37. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/grid_search.py +0 -0
  38. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/labels.py +0 -0
  39. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/result.py +0 -0
  40. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/runner.py +0 -0
  41. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/scoring.py +0 -0
  42. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/seasonality_search.py +0 -0
  43. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/settings.py +0 -0
  44. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/autotune/window_select.py +0 -0
  45. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/__init__.py +0 -0
  46. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/_output.py +0 -0
  47. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
  48. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
  49. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/autotune.md +0 -0
  50. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/cli.md +0 -0
  51. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
  52. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
  53. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/overview.md +0 -0
  54. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/rules/project.md +0 -0
  55. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +0 -0
  56. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
  57. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
  58. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
  59. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/assets/claude/skills/dtk-tune/SKILL.md +0 -0
  60. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/__init__.py +0 -0
  61. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/autotune.py +0 -0
  62. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/clean.py +0 -0
  63. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/init.py +0 -0
  64. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/init_claude.py +0 -0
  65. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/osi.py +0 -0
  66. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/run.py +0 -0
  67. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/test_alert.py +0 -0
  68. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/tune.py +0 -0
  69. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/ui.py +0 -0
  70. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/commands/unlock.py +0 -0
  71. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/cli/main.py +0 -0
  72. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/__init__.py +0 -0
  73. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/metric_config.py +0 -0
  74. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/metric_io.py +0 -0
  75. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/profile.py +0 -0
  76. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/project_config.py +0 -0
  77. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/config/validator.py +0 -0
  78. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/core/__init__.py +0 -0
  79. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/core/interval.py +0 -0
  80. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/core/models.py +0 -0
  81. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/__init__.py +0 -0
  82. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/_sql_manager.py +0 -0
  83. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/clickhouse_manager.py +0 -0
  84. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/__init__.py +0 -0
  85. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_alert_states.py +0 -0
  86. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
  87. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_base.py +0 -0
  88. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_datapoints.py +0 -0
  89. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_detections.py +0 -0
  90. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_maintenance.py +0 -0
  91. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_metrics.py +0 -0
  92. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_schema.py +0 -0
  93. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/_tasks.py +0 -0
  94. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/internal_tables/manager.py +0 -0
  95. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/manager.py +0 -0
  96. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/mysql_manager.py +0 -0
  97. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/postgres_manager.py +0 -0
  98. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/database/tables.py +0 -0
  99. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/__init__.py +0 -0
  100. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/base.py +0 -0
  101. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/factory.py +0 -0
  102. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/seasonality.py +0 -0
  103. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/__init__.py +0 -0
  104. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/_windowed.py +0 -0
  105. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/autoreg.py +0 -0
  106. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/iqr.py +0 -0
  107. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/mad.py +0 -0
  108. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/manual_bounds.py +0 -0
  109. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/detectors/statistical/zscore.py +0 -0
  110. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/loaders/__init__.py +0 -0
  111. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/loaders/metric_loader.py +0 -0
  112. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/loaders/query_template.py +0 -0
  113. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/__init__.py +0 -0
  114. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/error_dispatch.py +0 -0
  115. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/__init__.py +0 -0
  116. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
  117. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/_base.py +0 -0
  118. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
  119. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/_load_step.py +0 -0
  120. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/_types.py +0 -0
  121. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/orchestration/task_manager/manager.py +0 -0
  122. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/reporting/__init__.py +0 -0
  123. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/reporting/assets/report.js +0 -0
  124. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/reporting/builder.py +0 -0
  125. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/reporting/html_report.py +0 -0
  126. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/__init__.py +0 -0
  127. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/errors.py +0 -0
  128. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/exporter.py +0 -0
  129. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/importer.py +0 -0
  130. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/osi_model.py +0 -0
  131. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/semantic/query_gen.py +0 -0
  132. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/__init__.py +0 -0
  133. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/assets/tune.js +0 -0
  134. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/config_writer.py +0 -0
  135. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/html.py +0 -0
  136. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/payload.py +0 -0
  137. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/tuning/server.py +0 -0
  138. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/__init__.py +0 -0
  139. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/html.py +0 -0
  140. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/jobs.py +0 -0
  141. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/metric_files.py +0 -0
  142. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/ui/overview.py +0 -0
  143. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/utils/__init__.py +0 -0
  144. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/utils/datetime_utils.py +0 -0
  145. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/utils/env_interpolation.py +0 -0
  146. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/utils/json_utils.py +0 -0
  147. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit/utils/stats.py +0 -0
  148. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit.egg-info/SOURCES.txt +0 -0
  149. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit.egg-info/dependency_links.txt +0 -0
  150. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit.egg-info/entry_points.txt +0 -0
  151. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit.egg-info/requires.txt +0 -0
  152. {detectkit-0.55.0 → detectkit-0.55.1}/detectkit.egg-info/top_level.txt +0 -0
  153. {detectkit-0.55.0 → detectkit-0.55.1}/pyproject.toml +0 -0
  154. {detectkit-0.55.0 → detectkit-0.55.1}/requirements.txt +0 -0
  155. {detectkit-0.55.0 → detectkit-0.55.1}/setup.cfg +0 -0
  156. {detectkit-0.55.0 → detectkit-0.55.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.55.0
3
+ Version: 0.55.1
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
4
4
  A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
5
5
  """
6
6
 
7
- __version__ = "0.55.0"
7
+ __version__ = "0.55.1"
8
8
 
9
9
  from detectkit.core.interval import Interval
10
10
  from detectkit.core.models import ColumnDefinition, TableModel
@@ -458,7 +458,7 @@
458
458
  }
459
459
  `,t=document.createElement("style");t.setAttribute("data-dtk-ui",""),t.textContent=e,document.head.appendChild(t)}var ye=null;function Gt(e){return ye&&ye.isConnected||(ye=document.createElement("div"),ye.className="dtk-toasts",e.appendChild(ye)),ye}function de(e,t,n){let r=Gt(e),i=document.createElement("div");i.className=`dtk-toast dtk-toast-${t}`,i.textContent=n,r.appendChild(i),window.setTimeout(()=>{i.classList.add("dtk-toast-out"),window.setTimeout(()=>i.remove(),220)},5e3)}function ke(e,t,n,r){let i="dtk-ui-tile"+(r!=null&&r.err?" err":""),o=r!=null&&r.warn?"dtk-ui-tile-sub warn":"dtk-ui-tile-sub";return`<div class="${i}"><div class="dtk-ui-tile-val">${O(e)}</div><div class="dtk-ui-tile-label">${O(t)}</div>`+(n?`<div class="${o}">${O(n)}</div>`:"")+"</div>"}function kt(e){let t=document.createElement("div");t.className="dtk-ui-tiles";let n=e.length,r=e.filter(c=>c.enabled).length,i=e.filter(c=>!c.pending),o=0,a=0,s=0,f=0,l=!1;for(let c of i)o+=c.alerts.anomaly,a+=c.alerts.no_data,c.alerts.anomaly>0&&s++,c.alerts.per_day!==null&&(f+=c.alerts.per_day,l=!0);let p=[];for(let c of i){if(!c.enabled)continue;if(c.last_point===null){p.push({m:c,lag:1/0});continue}let v=Oe(c.lag_seconds,c.loading_delay_seconds);v!==null&&c.interval_seconds>0&&v>2*c.interval_seconds&&p.push({m:c,lag:v})}p.sort((c,v)=>v.lag-c.lag);let h=p.length===0?void 0:`worst: ${p[0].m.name}${Number.isFinite(p[0].lag)?` (${Ne(p[0].lag)})`:" (no data)"}`;t.innerHTML=ke(`${r}/${n}`,"Metrics","enabled / total")+ke(Te(o),"Alerts in window",l?Ce(f):void 0)+ke(Te(a),"No-data events")+ke(Te(s),"Metrics alerting")+ke(Te(p.length),"Stale metrics",h,{warn:p.length>0,err:p.length>0});let m=i.filter(c=>c.quality!==null);if(m.length>0){let c=0,v=0,w=0,M=0,u=0;for(let g of m){let S=g.quality;S&&(c+=S.caught,v+=S.incidents_in_window,w+=S.false_alerts,M+=g.alerts.anomaly,g.budget>u&&(u=g.budget))}let x=v>0?c/v:null,b=M>0?w/M:null,k=b!==null&&u>0&&b>u;t.innerHTML+=ke(ge(x),"Labeled recall",`${m.length} metric(s) labeled`)+ke(ge(b),"False-alert rate",k?`\u25B2 over ${ge(u)} budget`:void 0,{warn:k})}return t}var Re="untagged";function Xt(e){let t=new Map,n=(r,i)=>{let o=t.get(r);o||(o={tag:r,count:0,alerts:0,perDaySum:0,havePerDay:!1},t.set(r,o)),o.count++,o.alerts+=i.alerts.anomaly,i.alerts.per_day!==null&&(o.perDaySum+=i.alerts.per_day,o.havePerDay=!0)};for(let r of e)if(r.tags.length===0)n(Re,r);else for(let i of r.tags)n(i,r);return[...t.values()].sort((r,i)=>i.count-r.count||r.tag.localeCompare(i.tag))}function xt(e,t,n){let r=document.createElement("div");r.className="dtk-ui-tags";let i=document.createElement("button");i.type="button",i.className="dtk-ui-tag"+(t===null?" on":""),i.innerHTML=`<span class="dtk-ui-tag-name">All</span><span class="dtk-ui-tag-n">${e.length}</span>`,i.onclick=()=>n(null),r.appendChild(i);for(let o of Xt(e)){let a=document.createElement("button");a.type="button",a.className="dtk-ui-tag"+(t===o.tag?" on":"");let s=o.havePerDay?` \xB7 ${Ce(o.perDaySum)}`:"";a.innerHTML=`<span class="dtk-ui-tag-name">${O(o.tag===Re?Re:`#${o.tag}`)}</span><span class="dtk-ui-tag-n">${o.count} metric${o.count===1?"":"s"}</span><span class="dtk-ui-tag-n">${o.alerts} alert${o.alerts===1?"":"s"}</span>`+(s?`<span class="dtk-ui-tag-sub">${O(s)}</span>`:""),a.onclick=()=>n(o.tag),r.appendChild(a)}return r}var Zt={"--term-bg":"#211e1a","--clay":"#d15b36","--st-anomaly":"#d63232","--st-recovery":"#36a64f","--st-nodata":"#f0ad4e","--st-error":"#5a7a8c","--faint":"#9a9384","--muted":"#6e675b","--border":"#332f29","--term-border":"#332f29"};function Ae(e){return getComputedStyle(document.documentElement).getPropertyValue(e).trim()||Zt[e]||"#888"}function en(e){let t=e.replace("#","").trim();t.length===3&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);let n=parseInt(t,16);return t.length!==6||Number.isNaN(n)?[209,91,54]:[n>>16&255,n>>8&255,n&255]}function wt(e,t){let[n,r,i]=en(e);return`rgba(${n},${r},${i},${t})`}var Gn=Number.isFinite;var Et=140,Ct=30;function St(e,t,n){let r=Math.max(1,window.devicePixelRatio||1);e.style.width=`${Et}px`,e.style.height=`${Ct}px`,e.width=Math.round(Et*r),e.height=Math.round(Ct*r);let i=e.getContext("2d");if(!i||(i.clearRect(0,0,e.width,e.height),t.length===0))return;let o=3*r,a=e.width,s=e.height,f=t[0].t,l=t[t.length-1].t,p=l-f||1,h=b=>o+(b-f)/p*Math.max(1,a-2*o),m=1/0,c=-1/0;for(let b of t)b.v!==null&&Number.isFinite(b.v)&&(b.v<m&&(m=b.v),b.v>c&&(c=b.v));let v=!Number.isFinite(m)||!Number.isFinite(c);v&&(m=0,c=1),c<=m&&(c=m+1);let w=b=>s-o-(b-m)/(c-m)*Math.max(1,s-2*o);if(v){let b=s/2;i.strokeStyle=wt(Ae("--faint"),.5),i.lineWidth=1*r,i.setLineDash([2*r,2*r]),i.beginPath(),i.moveTo(o,b),i.lineTo(a-o,b),i.stroke(),i.setLineDash([]);return}i.strokeStyle=Ae("--term-text"),i.lineWidth=1*r,i.lineJoin="round",i.beginPath();let M=!1;for(let b of t){if(b.v===null||!Number.isFinite(b.v)){M=!1;continue}let k=h(b.t),g=w(b.v);M?i.lineTo(k,g):(i.moveTo(k,g),M=!0)}if(i.stroke(),n.length===0)return;let u=[];for(let b of t)b.v!==null&&Number.isFinite(b.v)&&u.push([b.t,b.v]);let x=b=>{if(u.length===0)return null;if(b<=u[0][0])return u[0][1];if(b>=u[u.length-1][0])return u[u.length-1][1];for(let k=1;k<u.length;k++){let[g,S]=u[k];if(b<=g){let[N,L]=u[k-1],_=g===N?0:(b-N)/(g-N);return L+(S-L)*_}}return u[u.length-1][1]};i.fillStyle=Ae("--st-anomaly");for(let b of n){if(b<f||b>l)continue;let k=x(b);k!==null&&(i.beginPath(),i.arc(h(b),w(k),2*r,0,Math.PI*2),i.fill())}}var Pe='<span class="dtk-ui-pending">\xB7\xB7\xB7</span>';function Se(e){return{name:e.name,dir:e.dir,file:e.file,tags:e.tags,enabled:e.enabled,interval_seconds:e.interval_seconds,loading_delay_seconds:0,detectors:[],alert_rule:null,last_point:null,first_point_in_window:null,lag_seconds:null,locked:!1,points:0,flagged:0,anomaly_rate:null,alerts:{anomaly:0,recovery:0,no_data:0,per_day:null,last_ts:null},quality:null,budget:0,spark:[],spark_anoms:[],error:null,pending:!0}}var Ke={alerts:"desc",name:"asc",rate:"desc",freshness:"desc"};function Tt(e){var a,s;if(e.pending)return{color:"var(--faint)",title:"loading\u2026",rank:0};if(!e.enabled)return{color:"var(--faint)",title:"disabled",rank:-1};if(e.last_point===null)return{color:"var(--st-anomaly)",title:"no datapoints loaded yet",rank:1/0};let t=(a=e.loading_delay_seconds)!=null?a:0,n=(s=Oe(e.lag_seconds,e.loading_delay_seconds))!=null?s:0,r=e.interval_seconds>0?n/e.interval_seconds:0,i=t>0?` \xB7 maturity delay ${Ne(t)} excluded`:"",o=`lag ${Ne(n)} (${r.toFixed(1)}\xD7 interval)${i} \xB7 last point ${Fe(e.last_point)} UTC`;return r<2?{color:"var(--st-recovery)",title:o,rank:n}:r<6?{color:"var(--st-nodata)",title:o,rank:n}:{color:"var(--st-anomaly)",title:o,rank:n}}function tn(e){return e.length===0?"":`<div class="dtk-ui-tagchips">${e.map(t=>`<span class="dtk-ui-tagchip">${O(t)}</span>`).join("")}</div>`}function nn(e){let t=e.quality;if(!t)return'<span class="dtk-ui-quality empty">\u2014</span>';let n=`Incidents: ${t.incidents} (${t.incidents_in_window} in window) \xB7 caught ${t.caught} \xB7 false alerts ${t.false_alerts} \xB7 reviewed ${t.reviewed} (valid ${t.reviewed_valid}, false ${t.reviewed_false}) \xB7 ${t.labels_file}`;return`<span class="dtk-ui-quality" title="${O(n)}"><span class="dtk-ui-quality-chip">R <b>${O(ge(t.recall))}</b></span> \xB7 <span class="dtk-ui-quality-chip">FDR <b>${O(ge(t.fdr))}</b></span> \xB7 <span class="dtk-ui-quality-chip">\u2713${t.reviewed_valid}</span></span>`}function on(e){let t=e.alert_rule?`min_detectors=${e.alert_rule.min_detectors} \xB7 direction=${e.alert_rule.direction} \xB7 consecutive=${e.alert_rule.consecutive} (${e.alert_rule.enabled}/${e.alert_rule.configs} config(s) enabled)`:"no alerting configured";return`detectors: ${e.detectors.join(", ")||"\u2014"}
460
460
  alert rule: ${t}
461
- file: ${e.file}`}function rn(e,t,n,r){let i=document.createElement("tr");i.className="dtk-ui-row"+(e.enabled?"":" disabled")+(e.error?" errored":"")+(e.pending?" pending":"");let o=Tt(e),a=document.createElement("td");a.className="dtk-ui-dotcell",a.innerHTML=`<span class="dtk-ui-dot" style="background:${o.color}" title="${O(o.title)}"></span>`,i.appendChild(a);let s=document.createElement("td");s.className="dtk-ui-namecell";let f=e.error?`<span class="dtk-ui-err-badge" title="${O(e.error)}">!</span>`:"";s.title=on(e),s.innerHTML=`<span class="dtk-ui-name">${O(e.name)}</span>${f}${tn(e.tags)}`,i.appendChild(s);let l=document.createElement("td");l.innerHTML=`<span class="dtk-ui-interval">${O(tt(e.interval_seconds))}</span>`,i.appendChild(l);let p=document.createElement("td");if(p.className="dtk-ui-sparkcell",e.pending)p.innerHTML='<span class="dtk-ui-spark-loading">loading\u2026</span>';else if(e.spark.length===0)p.innerHTML='<span class="dtk-ui-spark-empty">no data yet</span>';else{let g=document.createElement("canvas");g.className="dtk-spark",p.appendChild(g),r.push({canvas:g,points:e.spark.map(([S,N])=>({t:S,v:N})),anoms:e.spark_anoms})}i.appendChild(p);let h=document.createElement("td");if(h.className="dtk-ui-alertscell",e.pending)h.innerHTML=Pe;else{let g=e.quality!==null&&e.quality.fdr!==null&&e.quality.fdr>e.budget,S="dtk-ui-alerts-n"+(e.alerts.anomaly>0?" hasany":"")+(g?" overbudget":""),N=e.alerts.per_day!==null?`<span class="dtk-ui-alerts-sub">\xB7 ${O(Ce(e.alerts.per_day))}</span>`:"";h.innerHTML=`<span class="${S}">${e.alerts.anomaly}</span>${N}`}i.appendChild(h);let m=document.createElement("td");e.pending?m.innerHTML=Pe:e.alerts.last_ts!==null?m.innerHTML=`<span class="dtk-ui-lastalert" title="${O(Fe(e.alerts.last_ts))} UTC">${O($e(t,e.alerts.last_ts))}</span>`:m.innerHTML='<span class="dtk-ui-lastalert">\u2014</span>',i.appendChild(m);let c=document.createElement("td");c.innerHTML=e.pending?Pe:`<span class="dtk-ui-rate">${O(ge(e.anomaly_rate))}</span>`,i.appendChild(c);let v=document.createElement("td");v.innerHTML=e.pending?Pe:nn(e),i.appendChild(v);let w=document.createElement("td");w.innerHTML=e.locked?'<span class="dtk-ui-lock" title="pipeline lock currently held for this metric">LOCK</span>':"",i.appendChild(w);let M=document.createElement("td");M.className="dtk-ui-actionscell";let u=document.createElement("button");u.type="button",u.className="dtk-ui-actionbtn",u.textContent="Open",u.onclick=()=>n.onOpen(e.name);let x=document.createElement("button");x.type="button",x.className="dtk-ui-actionbtn",x.textContent="Tune",x.onclick=()=>n.onTune(e.name);let b=document.createElement("button");b.type="button",b.className="dtk-ui-actionbtn",b.textContent="Run",b.onclick=()=>n.onRun(e.name);let k=document.createElement("button");return k.type="button",k.className="dtk-ui-actionbtn",k.textContent="Edit",k.onclick=()=>n.onEdit(e.name),M.append(u,x,b,k),i.appendChild(M),i}function Mt(e,t){var n;switch(t){case"alerts":return e.alerts.anomaly;case"name":return e.name.toLowerCase();case"rate":return(n=e.anomaly_rate)!=null?n:-1;case"freshness":return Tt(e).rank}}function an(e,t){let n=e.filter(o=>o.enabled),r=e.filter(o=>!o.enabled),i=t.dir==="asc"?1:-1;return n.sort((o,a)=>{let s=Mt(o,t.key),f=Mt(a,t.key);return s<f?-1*i:s>f?1*i:o.name.localeCompare(a.name)}),r.sort((o,a)=>o.name.localeCompare(a.name)),[...n,...r]}var sn=[{label:"\u25CF",key:"freshness"},{label:"Name",key:"name"},{label:"Interval",key:null},{label:"Trend",key:null},{label:"Alerts",key:"alerts"},{label:"Last alert",key:null},{label:"Rate",key:"rate"},{label:"Quality",key:null},{label:"",key:null},{label:"",key:null}];function ln(e,t){let n=document.createElement("tr");for(let r of sn){let i=document.createElement("th");if(r.key){i.className="dtk-ui-th";let o=e.key===r.key?`<span class="dtk-ui-th-arrow">${e.dir==="asc"?"\u25B5":"\u25BE"}</span>`:"";i.innerHTML=`${O(r.label)}${o}`,i.onclick=()=>t.onSortChange(r.key)}else i.textContent=r.label;n.appendChild(i)}return n}function dn(e){return e===""?"metrics/":`metrics/${e}/`}function Nt(e,t,n,r){var f;let i=[],o=document.createElement("div");if(o.className="dtk-ui-table-wrap",e.length===0)return o.innerHTML='<div class="dtk-ui-empty">No metrics match the current filter.</div>',{el:o,paint:()=>{}};let a=ot(e,l=>l.dir),s=[...a.keys()].sort((l,p)=>l===p?0:l===""?-1:p===""?1:l.localeCompare(p));for(let l of s){let p=(f=a.get(l))!=null?f:[],h=document.createElement("div");h.className="dtk-ui-group";let m=p.reduce((u,x)=>u+x.alerts.anomaly,0),c=document.createElement("div");c.className="dtk-ui-group-head",c.innerHTML=`<span class="dtk-ui-group-name">${O(dn(l))}</span><span class="dtk-ui-group-sub">${p.length} metric${p.length===1?"":"s"} \xB7 ${m} alert${m===1?"":"s"}</span>`,h.appendChild(c);let v=document.createElement("table");v.className="dtk-ui-table";let w=document.createElement("thead");w.appendChild(ln(t,r)),v.appendChild(w);let M=document.createElement("tbody");for(let u of an(p,t))M.appendChild(rn(u,n,r,i));v.appendChild(M),h.appendChild(v),o.appendChild(h)}return{el:o,paint:()=>{for(let l of i)St(l.canvas,l.points,l.anoms)}}}var cn='a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';function He(e,t){let n=document.createElement("div");n.className="dtk-ui-overlay";let r=document.createElement("div");r.className="dtk-ui-overlay-modal"+(t.modalClass?` ${t.modalClass}`:""),r.setAttribute("role","dialog"),r.setAttribute("aria-modal","true"),n.appendChild(r);let i=document.activeElement instanceof HTMLElement?document.activeElement:null;function o(l){let p=Array.from(r.querySelectorAll(cn)).filter(w=>w.offsetParent!==null);if(p.length===0)return;let h=p[0],m=p[p.length-1],c=document.activeElement,v=c instanceof HTMLElement&&r.contains(c);l.shiftKey&&(!v||c===h)?(l.preventDefault(),m.focus()):!l.shiftKey&&(!v||c===m)&&(l.preventDefault(),h.focus())}function a(l){l.key==="Escape"?t.onRequestClose():l.key==="Tab"&&o(l)}n.addEventListener("click",l=>{l.target===n&&t.onRequestClose()}),document.addEventListener("keydown",a),e.appendChild(n);let s=!1;function f(){s||(s=!0,document.removeEventListener("keydown",a),n.remove(),i==null||i.focus())}return{backdrop:n,modal:r,close:f}}function Rt(e,t,n,r){let i=He(e,{onRequestClose:()=>c()}),{modal:o}=i,a=document.createElement("div");a.className="dtk-ui-overlay-head",a.innerHTML=`<span><span class="dtk-ui-overlay-title">${O(t)}</span><span class="dtk-ui-overlay-sub">window: ${O(n)}</span></span>`;let s=document.createElement("div");s.className="dtk-ui-overlay-actions";let f=document.createElement("button");f.type="button",f.className="dtk-ui-btn",f.textContent="Tune",f.onclick=()=>r.onTune(t);let l=document.createElement("button");l.type="button",l.className="dtk-ui-overlay-close",l.textContent="\u2715",l.title="Close (Esc)",s.append(f,l),a.appendChild(s),o.appendChild(a);let p=document.createElement("div");p.className="dtk-ui-overlay-body";let h=document.createElement("div");h.className="dtk-ui-overlay-loading",h.innerHTML=`<span class="dtk-ui-overlay-spinner"></span><span>Building the report for <b>${O(t)}</b>\u2026</span>`,p.appendChild(h);let m=document.createElement("iframe");m.title=`detectkit report \u2014 ${t}`,m.style.visibility="hidden",m.addEventListener("load",()=>{h.style.display="none",m.style.visibility="visible"}),m.src=Be(t,n),p.appendChild(m),o.appendChild(p);function c(){i.close(),r.onClose()}return l.onclick=c,{setWindow(v){let w=a.querySelector(".dtk-ui-overlay-sub");w&&(w.textContent=`window: ${v}`),h.style.display="",m.style.visibility="hidden",m.src=Be(t,v)},close:c}}var Lt=/^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?$/,_t="dtk-ui-run-select-options";function Dt(e,t){let n=document.createElement("div");n.className="dtk-ui-drawer-backdrop";let r=document.createElement("div");r.className="dtk-ui-drawer";let i=document.createElement("div");i.className="dtk-ui-drawer-head",i.innerHTML='<span class="dtk-ui-drawer-title">Run pipeline</span>';let o=document.createElement("button");o.type="button",o.className="dtk-ui-drawer-close",o.textContent="\u2715",i.appendChild(o),r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-drawer-body",r.appendChild(a);let s=document.createElement("div");s.className="dtk-ui-field";let f=document.createElement("datalist");f.id=_t;let l=document.createElement("input");l.type="text",l.className="dtk-ui-input",l.placeholder="metric name, tag:x, glob, or *",l.value="*",l.setAttribute("list",_t),s.innerHTML='<span class="dtk-ui-field-label">Select</span>',s.append(l,f),a.appendChild(s);let p=document.createElement("div");p.className="dtk-ui-field",p.innerHTML='<span class="dtk-ui-field-label">Steps</span>';let h=document.createElement("div");h.className="dtk-ui-checks";let m={};for(let T of["load","detect","alert"]){let Y=document.createElement("label");Y.className="dtk-ui-check";let W=document.createElement("input");W.type="checkbox",W.checked=!0,W.onchange=J,Y.append(W,document.createTextNode(T)),h.appendChild(Y),m[T]=W}p.appendChild(h),a.appendChild(p);let c=document.createElement("div");c.className="dtk-ui-row2";let v=document.createElement("div");v.className="dtk-ui-field",v.innerHTML='<span class="dtk-ui-field-label">From</span>';let w=document.createElement("input");w.type="text",w.className="dtk-ui-input",w.placeholder="YYYY-MM-DD [HH:MM:SS]",w.oninput=J,v.appendChild(w);let M=document.createElement("div");M.className="dtk-ui-field",M.innerHTML='<span class="dtk-ui-field-label">To</span>';let u=document.createElement("input");u.type="text",u.className="dtk-ui-input",u.placeholder="YYYY-MM-DD [HH:MM:SS]",u.oninput=J,M.appendChild(u),c.append(v,M),a.appendChild(c);let x=document.createElement("div");x.className="dtk-ui-checks";let b=document.createElement("label");b.className="dtk-ui-check";let k=document.createElement("input");k.type="checkbox",b.append(k,document.createTextNode("force (skip lock check)"));let g=document.createElement("label");g.className="dtk-ui-check";let S=document.createElement("input");S.type="checkbox",g.append(S,document.createTextNode("full refresh")),x.append(b,g),a.appendChild(x);let N=document.createElement("div");N.className="dtk-ui-btnrow";let L=document.createElement("button");L.type="button",L.className="dtk-ui-btn primary",L.textContent="Run";let _=document.createElement("button");_.type="button",_.className="dtk-ui-btn",_.textContent="Autotune";let A=document.createElement("button");A.type="button",A.className="dtk-ui-btn danger",A.textContent="Unlock",N.append(L,_,A),a.appendChild(N);let I=document.createElement("div");I.className="dtk-ui-reason",a.appendChild(I);let z=document.createElement("div");z.className="dtk-ui-field",z.innerHTML='<span class="dtk-ui-field-label">Log</span>';let K=document.createElement("div");K.className="dtk-ui-log";let q=document.createElement("div");q.className="dtk-ui-log-body";let G=document.createElement("div");G.className="dtk-ui-log-line",K.append(q,G),z.appendChild(K),a.appendChild(z);let U=[];function B(){q.innerHTML=U.length===0?'<span class="dtk-ui-log-empty">no output yet</span>':U.map(O).join("<br>")}B();function le(){return K.scrollTop+K.clientHeight>=K.scrollHeight-24}function ne(){return Object.keys(m).filter(T=>m[T].checked)}function J(){let T=t.isPipelineBusy(),Y=l.value.trim(),W=w.value.trim()===""||Lt.test(w.value.trim()),Q=u.value.trim()===""||Lt.test(u.value.trim()),oe="";T.busy?oe=T.reason:Y===""?oe="select is required":!W||!Q?oe="from/to must be YYYY-MM-DD or YYYY-MM-DD HH:MM:SS":ne().length===0&&(oe="pick at least one step to run"),I.textContent=oe,L.disabled=oe!=="",_.disabled=T.busy||Y===""||!W||!Q,A.disabled=T.busy||Y===""}function V(){return{select:l.value.trim(),steps:ne(),from:w.value.trim()||null,to:u.value.trim()||null,full_refresh:S.checked,force:k.checked}}L.onclick=()=>t.submitRun(V()),_.onclick=()=>t.submitAutotune({select:l.value.trim(),from:w.value.trim()||null,to:u.value.trim()||null}),A.onclick=()=>{let T=l.value.trim();window.confirm(`Unlock the pipeline lock for "${T}"? Only do this if you're sure no dtk process is actually running against it.`)&&t.submitUnlock({select:T})};function E(){n.classList.remove("open"),r.classList.remove("open")}return o.onclick=E,n.onclick=E,e.append(n,r),{el:r,open(T){T&&(l.value=T),n.classList.add("open"),r.classList.add("open"),J()},close:E,isOpen(){return r.classList.contains("open")},refreshOptions(){f.innerHTML=["*",...t.getSelectOptions()].map(T=>`<option value="${O(T)}"></option>`).join("")},refreshBusyState:J,resetLog(){U=[],B(),G.textContent="",G.className="dtk-ui-log-line"},appendLog(T){if(T.length===0)return;let Y=le();U.push(...T),B(),Y&&(K.scrollTop=K.scrollHeight)},setLogStatus(T,Y){if(T==="running")return;let W=T==="done"&&Y===0?"exit-ok":T==="stopped"?"exit-stop":"exit-fail";G.className=`dtk-ui-log-line ${W}`,G.textContent=`\u2500\u2500 ${T} (exit ${Y!=null?Y:"?"}) \u2500\u2500`}}}function un(e){return e==="done"?"var(--st-recovery)":e==="failed"?"var(--st-anomaly)":e==="running"?"var(--clay)":"var(--faint)"}function $t(e,t){let n=document.createElement("div");n.className="dtk-ui-drawer-backdrop";let r=document.createElement("div");r.className="dtk-ui-drawer";let i=document.createElement("div");i.className="dtk-ui-drawer-head",i.innerHTML='<span class="dtk-ui-drawer-title">Jobs</span>';let o=document.createElement("button");o.type="button",o.className="dtk-ui-drawer-close",o.textContent="\u2715",i.appendChild(o),r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-drawer-body",r.appendChild(a);let s=document.createElement("div");s.className="dtk-ui-joblist",a.appendChild(s);function f(){n.classList.remove("open"),r.classList.remove("open")}return o.onclick=f,n.onclick=f,e.append(n,r),{el:r,open(){n.classList.add("open"),r.classList.add("open")},close:f,isOpen(){return r.classList.contains("open")},render(l,p,h){var m;if(l.length===0){s.innerHTML='<div class="dtk-ui-empty">No jobs yet.</div>';return}s.innerHTML="";for(let c of l){let v=document.createElement("div");v.className="dtk-ui-jobrow"+(c.id===h?" active":"");let w=c.status==="running"?" pulse":"",M=nt(c.started_at,(m=c.finished_at)!=null?m:p),u=document.createElement("div");u.className="dtk-ui-jobrow-top",u.innerHTML=`<span class="dtk-ui-jobrow-status"><span class="dtk-ui-jobrow-dot${w}" style="background:${un(c.status)}"></span>${O(c.kind)} \xB7 ${O(c.status)}</span><span class="dtk-ui-jobrow-meta">${O($e(p,c.started_at))} \xB7 ${O(M)}</span>`,v.appendChild(u);let x=document.createElement("div");x.className="dtk-ui-jobrow-label",x.textContent=c.label,v.appendChild(x);let b=document.createElement("div");if(b.className="dtk-ui-jobrow-actions",c.status==="running"){let k=document.createElement("button");k.type="button",k.className="dtk-ui-actionbtn",k.textContent="Stop",k.onclick=g=>{g.stopPropagation(),t.onStop(c.id)},b.appendChild(k)}if(c.kind==="tune"&&c.url){let k=document.createElement("a");k.className="dtk-ui-joblink",k.href=c.url,k.target="_blank",k.rel="noopener",k.textContent="Open tuner",k.onclick=g=>g.stopPropagation(),b.appendChild(k)}b.childElementCount>0&&v.appendChild(b),v.onclick=()=>t.onFollow(c.id),s.appendChild(v)}}}}var pn=/^(true|false|null|yes|no|on|off|~)$/i,mn=/^[A-Za-z0-9_][A-Za-z0-9_.\-]*$/,fn=/^[-+.]?\d/;function Ot(e){return!(!mn.test(e)||pn.test(e)||!Number.isNaN(Number(e))||fn.test(e))}function Le(e){return JSON.stringify(e)}function bn(e){var o;let r=(e.endsWith(`
461
+ file: ${e.file}`}function rn(e,t,n,r){let i=document.createElement("tr");i.className="dtk-ui-row"+(e.enabled?"":" disabled")+(e.error?" errored":"")+(e.pending?" pending":"");let o=Tt(e),a=document.createElement("td");a.className="dtk-ui-dotcell",a.innerHTML=`<span class="dtk-ui-dot" style="background:${o.color}" title="${O(o.title)}"></span>`,i.appendChild(a);let s=document.createElement("td");s.className="dtk-ui-namecell";let f=e.error?`<span class="dtk-ui-err-badge" title="${O(e.error)}">!</span>`:"";s.title=on(e),s.innerHTML=`<span class="dtk-ui-name">${O(e.name)}</span>${f}${tn(e.tags)}`,i.appendChild(s);let l=document.createElement("td");l.innerHTML=`<span class="dtk-ui-interval">${O(tt(e.interval_seconds))}</span>`,i.appendChild(l);let p=document.createElement("td");if(p.className="dtk-ui-sparkcell",e.pending)p.innerHTML='<span class="dtk-ui-spark-loading">loading\u2026</span>';else if(e.spark.length===0)p.innerHTML='<span class="dtk-ui-spark-empty">no data yet</span>';else{let g=document.createElement("canvas");g.className="dtk-spark",p.appendChild(g),r.push({canvas:g,points:e.spark.map(([S,N])=>({t:S,v:N})),anoms:e.spark_anoms})}i.appendChild(p);let h=document.createElement("td");if(h.className="dtk-ui-alertscell",e.pending)h.innerHTML=Pe;else{let g=e.quality!==null&&e.quality.fdr!==null&&e.quality.fdr>e.budget,S="dtk-ui-alerts-n"+(e.alerts.anomaly>0?" hasany":"")+(g?" overbudget":""),N=e.alerts.per_day!==null?`<span class="dtk-ui-alerts-sub">\xB7 ${O(Ce(e.alerts.per_day))}</span>`:"";h.innerHTML=`<span class="${S}">${e.alerts.anomaly}</span>${N}`}i.appendChild(h);let m=document.createElement("td");e.pending?m.innerHTML=Pe:e.alerts.last_ts!==null?m.innerHTML=`<span class="dtk-ui-lastalert" title="${O(Fe(e.alerts.last_ts))} UTC">${O($e(t,e.alerts.last_ts))}</span>`:m.innerHTML='<span class="dtk-ui-lastalert">\u2014</span>',i.appendChild(m);let c=document.createElement("td");c.innerHTML=e.pending?Pe:`<span class="dtk-ui-rate">${O(ge(e.anomaly_rate))}</span>`,i.appendChild(c);let v=document.createElement("td");v.innerHTML=e.pending?Pe:nn(e),i.appendChild(v);let w=document.createElement("td");w.innerHTML=e.locked?'<span class="dtk-ui-lock" title="pipeline lock currently held for this metric">LOCK</span>':"",i.appendChild(w);let M=document.createElement("td");M.className="dtk-ui-actionscell";let u=document.createElement("button");u.type="button",u.className="dtk-ui-actionbtn",u.textContent="Open",u.onclick=()=>n.onOpen(e.name);let x=document.createElement("button");x.type="button",x.className="dtk-ui-actionbtn",x.textContent="Tune",x.onclick=()=>n.onTune(e.name);let b=document.createElement("button");b.type="button",b.className="dtk-ui-actionbtn",b.textContent="Run",b.onclick=()=>n.onRun(e.name);let k=document.createElement("button");return k.type="button",k.className="dtk-ui-actionbtn",k.textContent="Edit",k.onclick=()=>n.onEdit(e.name),M.append(u,x,b,k),i.appendChild(M),i}function Mt(e,t){var n;switch(t){case"alerts":return e.alerts.anomaly;case"name":return e.name.toLowerCase();case"rate":return(n=e.anomaly_rate)!=null?n:-1;case"freshness":return Tt(e).rank}}function an(e,t){let n=e.filter(o=>o.enabled),r=e.filter(o=>!o.enabled),i=t.dir==="asc"?1:-1;return n.sort((o,a)=>{let s=Mt(o,t.key),f=Mt(a,t.key);return s<f?-1*i:s>f?1*i:o.name.localeCompare(a.name)}),r.sort((o,a)=>o.name.localeCompare(a.name)),[...n,...r]}var sn=[{label:"\u25CF",key:"freshness"},{label:"Name",key:"name"},{label:"Interval",key:null},{label:"Trend",key:null},{label:"Alerts",key:"alerts"},{label:"Last alert",key:null},{label:"Rate",key:"rate"},{label:"Quality",key:null},{label:"",key:null},{label:"",key:null}];function ln(e,t){let n=document.createElement("tr");for(let r of sn){let i=document.createElement("th");if(r.key){i.className="dtk-ui-th";let o=e.key===r.key?`<span class="dtk-ui-th-arrow">${e.dir==="asc"?"\u25B5":"\u25BE"}</span>`:"";i.innerHTML=`${O(r.label)}${o}`,i.onclick=()=>t.onSortChange(r.key)}else i.textContent=r.label;n.appendChild(i)}return n}function dn(e){return e===""?"metrics/":`metrics/${e}/`}function Nt(e,t,n,r){var f;let i=[],o=document.createElement("div");if(o.className="dtk-ui-table-wrap",e.length===0)return o.innerHTML='<div class="dtk-ui-empty">No metrics match the current filter.</div>',{el:o,paint:()=>{}};let a=ot(e,l=>l.dir),s=[...a.keys()].sort((l,p)=>l===p?0:l===""?-1:p===""?1:l.localeCompare(p));for(let l of s){let p=(f=a.get(l))!=null?f:[],h=document.createElement("div");h.className="dtk-ui-group";let m=p.reduce((u,x)=>u+x.alerts.anomaly,0),c=document.createElement("div");c.className="dtk-ui-group-head",c.innerHTML=`<span class="dtk-ui-group-name">${O(dn(l))}</span><span class="dtk-ui-group-sub">${p.length} metric${p.length===1?"":"s"} \xB7 ${m} alert${m===1?"":"s"}</span>`,h.appendChild(c);let v=document.createElement("table");v.className="dtk-ui-table";let w=document.createElement("thead");w.appendChild(ln(t,r)),v.appendChild(w);let M=document.createElement("tbody");for(let u of an(p,t))M.appendChild(rn(u,n,r,i));v.appendChild(M),h.appendChild(v),o.appendChild(h)}return{el:o,paint:()=>{for(let l of i)St(l.canvas,l.points,l.anoms)}}}var cn='a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';function He(e,t){let n=document.createElement("div");n.className="dtk-ui-overlay";let r=document.createElement("div");r.className="dtk-ui-overlay-modal"+(t.modalClass?` ${t.modalClass}`:""),r.setAttribute("role","dialog"),r.setAttribute("aria-modal","true"),n.appendChild(r);let i=document.activeElement instanceof HTMLElement?document.activeElement:null;function o(l){let p=Array.from(r.querySelectorAll(cn)).filter(w=>w.offsetParent!==null);if(p.length===0)return;let h=p[0],m=p[p.length-1],c=document.activeElement,v=c instanceof HTMLElement&&r.contains(c);l.shiftKey&&(!v||c===h)?(l.preventDefault(),m.focus()):!l.shiftKey&&(!v||c===m)&&(l.preventDefault(),h.focus())}function a(l){l.key==="Escape"?t.onRequestClose():l.key==="Tab"&&o(l)}n.addEventListener("click",l=>{l.target===n&&t.onRequestClose()}),document.addEventListener("keydown",a),e.appendChild(n);let s=!1;function f(){s||(s=!0,document.removeEventListener("keydown",a),n.remove(),i==null||i.focus())}return{backdrop:n,modal:r,close:f}}function Rt(e,t,n,r){let i=He(e,{onRequestClose:()=>c()}),{modal:o}=i,a=document.createElement("div");a.className="dtk-ui-overlay-head",a.innerHTML=`<span><span class="dtk-ui-overlay-title">${O(t)}</span><span class="dtk-ui-overlay-sub">window: ${O(n)}</span></span>`;let s=document.createElement("div");s.className="dtk-ui-overlay-actions";let f=document.createElement("button");f.type="button",f.className="dtk-ui-btn",f.textContent="Tune",f.onclick=()=>r.onTune(t);let l=document.createElement("button");l.type="button",l.className="dtk-ui-overlay-close",l.textContent="\u2715",l.title="Close (Esc)",s.append(f,l),a.appendChild(s),o.appendChild(a);let p=document.createElement("div");p.className="dtk-ui-overlay-body";let h=document.createElement("div");h.className="dtk-ui-overlay-loading",h.innerHTML=`<span class="dtk-ui-overlay-spinner"></span><span>Building the report for <b>${O(t)}</b>\u2026</span>`,p.appendChild(h);let m=document.createElement("iframe");m.title=`detectkit report \u2014 ${t}`,m.style.visibility="hidden",m.addEventListener("load",()=>{h.style.display="none",m.style.visibility="visible"}),m.src=Be(t,n),p.appendChild(m),o.appendChild(p);function c(){i.close(),r.onClose()}return l.onclick=c,{setWindow(v){let w=a.querySelector(".dtk-ui-overlay-sub");w&&(w.textContent=`window: ${v}`),h.style.display="",m.style.visibility="hidden",m.src=Be(t,v)},close:c}}var Lt=/^\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?$/,_t="dtk-ui-run-select-options";function Dt(e,t){let n=document.createElement("div");n.className="dtk-ui-drawer-backdrop";let r=document.createElement("div");r.className="dtk-ui-drawer";let i=document.createElement("div");i.className="dtk-ui-drawer-head",i.innerHTML='<span class="dtk-ui-drawer-title">Run pipeline</span>';let o=document.createElement("button");o.type="button",o.className="dtk-ui-drawer-close",o.textContent="\u2715",i.appendChild(o),r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-drawer-body",r.appendChild(a);let s=document.createElement("div");s.className="dtk-ui-field";let f=document.createElement("datalist");f.id=_t;let l=document.createElement("input");l.type="text",l.className="dtk-ui-input",l.placeholder="metric name, tag:x, glob, or *",l.value="*",l.setAttribute("list",_t),s.innerHTML='<span class="dtk-ui-field-label">Select</span>',s.append(l,f),a.appendChild(s);let p=document.createElement("div");p.className="dtk-ui-field",p.innerHTML='<span class="dtk-ui-field-label">Steps</span>';let h=document.createElement("div");h.className="dtk-ui-checks";let m={};for(let T of["load","detect","alert"]){let Y=document.createElement("label");Y.className="dtk-ui-check";let W=document.createElement("input");W.type="checkbox",W.checked=!0,W.onchange=J,Y.append(W,document.createTextNode(T)),h.appendChild(Y),m[T]=W}p.appendChild(h),a.appendChild(p);let c=document.createElement("div");c.className="dtk-ui-row2";let v=document.createElement("div");v.className="dtk-ui-field",v.innerHTML='<span class="dtk-ui-field-label">From</span>';let w=document.createElement("input");w.type="text",w.className="dtk-ui-input",w.placeholder="YYYY-MM-DD [HH:MM:SS]",w.oninput=J,v.appendChild(w);let M=document.createElement("div");M.className="dtk-ui-field",M.innerHTML='<span class="dtk-ui-field-label">To</span>';let u=document.createElement("input");u.type="text",u.className="dtk-ui-input",u.placeholder="YYYY-MM-DD [HH:MM:SS]",u.oninput=J,M.appendChild(u),c.append(v,M),a.appendChild(c);let x=document.createElement("div");x.className="dtk-ui-checks";let b=document.createElement("label");b.className="dtk-ui-check";let k=document.createElement("input");k.type="checkbox",b.append(k,document.createTextNode("force (skip lock check)"));let g=document.createElement("label");g.className="dtk-ui-check";let S=document.createElement("input");S.type="checkbox",g.append(S,document.createTextNode("full refresh")),x.append(b,g),a.appendChild(x);let N=document.createElement("div");N.className="dtk-ui-btnrow";let L=document.createElement("button");L.type="button",L.className="dtk-ui-btn primary",L.textContent="Run";let _=document.createElement("button");_.type="button",_.className="dtk-ui-btn",_.textContent="Autotune";let P=document.createElement("button");P.type="button",P.className="dtk-ui-btn danger",P.textContent="Unlock",N.append(L,_,P),a.appendChild(N);let I=document.createElement("div");I.className="dtk-ui-reason",a.appendChild(I);let z=document.createElement("div");z.className="dtk-ui-field",z.innerHTML='<span class="dtk-ui-field-label">Log</span>';let K=document.createElement("div");K.className="dtk-ui-log";let q=document.createElement("div");q.className="dtk-ui-log-body";let X=document.createElement("div");X.className="dtk-ui-log-line",K.append(q,X),z.appendChild(K),a.appendChild(z);let U=[];function B(){q.innerHTML=U.length===0?'<span class="dtk-ui-log-empty">no output yet</span>':U.map(O).join("<br>")}B();function le(){return K.scrollTop+K.clientHeight>=K.scrollHeight-24}function ne(){return Object.keys(m).filter(T=>m[T].checked)}function J(){let T=t.isPipelineBusy(),Y=l.value.trim(),W=w.value.trim()===""||Lt.test(w.value.trim()),Q=u.value.trim()===""||Lt.test(u.value.trim()),oe="";T.busy?oe=T.reason:Y===""?oe="select is required":!W||!Q?oe="from/to must be YYYY-MM-DD or YYYY-MM-DD HH:MM:SS":ne().length===0&&(oe="pick at least one step to run"),I.textContent=oe,L.disabled=oe!=="",_.disabled=T.busy||Y===""||!W||!Q,P.disabled=T.busy||Y===""}function V(){return{select:l.value.trim(),steps:ne(),from:w.value.trim()||null,to:u.value.trim()||null,full_refresh:S.checked,force:k.checked}}L.onclick=()=>t.submitRun(V()),_.onclick=()=>t.submitAutotune({select:l.value.trim(),from:w.value.trim()||null,to:u.value.trim()||null}),P.onclick=()=>{let T=l.value.trim();window.confirm(`Unlock the pipeline lock for "${T}"? Only do this if you're sure no dtk process is actually running against it.`)&&t.submitUnlock({select:T})};function E(){n.classList.remove("open"),r.classList.remove("open")}return o.onclick=E,n.onclick=E,e.append(n,r),{el:r,open(T){T&&(l.value=T),n.classList.add("open"),r.classList.add("open"),J()},close:E,isOpen(){return r.classList.contains("open")},refreshOptions(){f.innerHTML=["*",...t.getSelectOptions()].map(T=>`<option value="${O(T)}"></option>`).join("")},refreshBusyState:J,resetLog(){U=[],B(),X.textContent="",X.className="dtk-ui-log-line"},appendLog(T){if(T.length===0)return;let Y=le();U.push(...T),B(),Y&&(K.scrollTop=K.scrollHeight)},setLogStatus(T,Y){if(T==="running")return;let W=T==="done"&&Y===0?"exit-ok":T==="stopped"?"exit-stop":"exit-fail";X.className=`dtk-ui-log-line ${W}`,X.textContent=`\u2500\u2500 ${T} (exit ${Y!=null?Y:"?"}) \u2500\u2500`}}}function un(e){return e==="done"?"var(--st-recovery)":e==="failed"?"var(--st-anomaly)":e==="running"?"var(--clay)":"var(--faint)"}function $t(e,t){let n=document.createElement("div");n.className="dtk-ui-drawer-backdrop";let r=document.createElement("div");r.className="dtk-ui-drawer";let i=document.createElement("div");i.className="dtk-ui-drawer-head",i.innerHTML='<span class="dtk-ui-drawer-title">Jobs</span>';let o=document.createElement("button");o.type="button",o.className="dtk-ui-drawer-close",o.textContent="\u2715",i.appendChild(o),r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-drawer-body",r.appendChild(a);let s=document.createElement("div");s.className="dtk-ui-joblist",a.appendChild(s);function f(){n.classList.remove("open"),r.classList.remove("open")}return o.onclick=f,n.onclick=f,e.append(n,r),{el:r,open(){n.classList.add("open"),r.classList.add("open")},close:f,isOpen(){return r.classList.contains("open")},render(l,p,h){var m;if(l.length===0){s.innerHTML='<div class="dtk-ui-empty">No jobs yet.</div>';return}s.innerHTML="";for(let c of l){let v=document.createElement("div");v.className="dtk-ui-jobrow"+(c.id===h?" active":"");let w=c.status==="running"?" pulse":"",M=nt(c.started_at,(m=c.finished_at)!=null?m:p),u=document.createElement("div");u.className="dtk-ui-jobrow-top",u.innerHTML=`<span class="dtk-ui-jobrow-status"><span class="dtk-ui-jobrow-dot${w}" style="background:${un(c.status)}"></span>${O(c.kind)} \xB7 ${O(c.status)}</span><span class="dtk-ui-jobrow-meta">${O($e(p,c.started_at))} \xB7 ${O(M)}</span>`,v.appendChild(u);let x=document.createElement("div");x.className="dtk-ui-jobrow-label",x.textContent=c.label,v.appendChild(x);let b=document.createElement("div");if(b.className="dtk-ui-jobrow-actions",c.status==="running"){let k=document.createElement("button");k.type="button",k.className="dtk-ui-actionbtn",k.textContent="Stop",k.onclick=g=>{g.stopPropagation(),t.onStop(c.id)},b.appendChild(k)}if(c.kind==="tune"&&c.url){let k=document.createElement("a");k.className="dtk-ui-joblink",k.href=c.url,k.target="_blank",k.rel="noopener",k.textContent="Open tuner",k.onclick=g=>g.stopPropagation(),b.appendChild(k)}b.childElementCount>0&&v.appendChild(b),v.onclick=()=>t.onFollow(c.id),s.appendChild(v)}}}}var pn=/^(true|false|null|yes|no|on|off|~)$/i,mn=/^[A-Za-z0-9_][A-Za-z0-9_.\-]*$/,fn=/^[-+.]?\d/;function Ot(e){return!(!mn.test(e)||pn.test(e)||!Number.isNaN(Number(e))||fn.test(e))}function Le(e){return JSON.stringify(e)}function bn(e){var o;let r=(e.endsWith(`
462
462
  `)?e.slice(0,-1):e).split(`
463
463
  `),i=(o=r[0])!=null?o:"";return!(i.length===0||/^\s/.test(i)||r.some(a=>a.trim().length===0))}function gn(e,t){let n=e.endsWith(`
464
464
  `),i=(n?e.slice(0,-1):e).split(`
@@ -468,7 +468,7 @@ file: ${e.file}`}function rn(e,t,n,r){let i=document.createElement("tr");i.class
468
468
  `))n.push(`# ${o}`);let r=We(e);return Object.keys(r).length===0?n.push("{}"):n.push(...Qe(r,"")),n.join(`
469
469
  `)+`
470
470
  `}var vn=new Set("SELECT FROM WHERE GROUP BY ORDER HAVING JOIN LEFT RIGHT INNER OUTER FULL CROSS ON AS AND OR NOT IN IS BETWEEN LIKE CASE WHEN THEN ELSE END LIMIT OFFSET WITH UNION ALL DISTINCT INTERVAL SECOND MINUTE HOUR DAY CAST NULL TRUE FALSE INSERT UPDATE DELETE SET VALUES OVER PARTITION DESC ASC".split(/\s+/)),Ve=/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}/g,Ge=/(--[^\n]*|\/\*[\s\S]*?\*\/)|('(?:''|[^'])*')|(\b\d+(?:\.\d+)?\b)|([A-Za-z_][A-Za-z0-9_]*)/g;function qt(e){let t=[],n=0;Ge.lastIndex=0;let r;for(;r=Ge.exec(e);){r.index>n&&t.push({start:n,end:r.index,cls:null});let i=r.index,o=Ge.lastIndex;if(r[1]!==void 0)t.push({start:i,end:o,cls:"dtk-sql-cmt"});else if(r[2]!==void 0)t.push({start:i,end:o,cls:"dtk-sql-str"});else if(r[3]!==void 0)t.push({start:i,end:o,cls:"dtk-sql-num"});else if(r[4]!==void 0){let a=r[4],s;vn.has(a.toUpperCase())?s="dtk-sql-kw":/^\s*\(/.test(e.slice(o))?s="dtk-sql-fn":s=null,t.push({start:i,end:o,cls:s})}n=o}return n<e.length&&t.push({start:n,end:e.length,cls:null}),t}function yn(e){let t=[];Ve.lastIndex=0;let n;for(;n=Ve.exec(e);)t.push({start:n.index,end:Ve.lastIndex,text:n[0]});if(t.length===0)return qt(e).map(s=>({text:e.slice(s.start,s.end),cls:s.cls}));let r=e;for(let s of t)r=r.slice(0,s.start)+"_".repeat(s.end-s.start)+r.slice(s.end);let i=qt(r),o=[],a=0;for(let s of i){let f=s.start;for(;a<t.length&&t[a].start<s.end;){let l=t[a];l.start>f&&o.push({text:e.slice(f,l.start),cls:s.cls}),o.push({text:l.text,cls:"dtk-sql-jinja"}),f=l.end,a++}f<s.end&&o.push({text:e.slice(f,s.end),cls:s.cls})}return o}function kn(e){return yn(e).map(n=>n.cls?`<span class="${n.cls}">${O(n.text)}</span>`:O(n.text)).join("")+`
471
- `}function Ft(e){let t=document.createElement("div");t.className="dtk-ui-sqled";let n=document.createElement("pre");n.className="dtk-ui-sqled-hl",n.setAttribute("aria-hidden","true");let r=document.createElement("code");n.appendChild(r);let i=document.createElement("textarea");i.className="dtk-ui-sqled-ta",i.spellcheck=!1,i.autocapitalize="off",e.ariaLabel&&i.setAttribute("aria-label",e.ariaLabel),t.append(n,i);function o(f){r.innerHTML=kn(f)}function a(){n.scrollTop=i.scrollTop,n.scrollLeft=i.scrollLeft}i.value=e.value,o(e.value),i.addEventListener("input",()=>{o(i.value),a(),e.onInput(i.value)}),i.addEventListener("scroll",a),i.addEventListener("keydown",f=>{if(f.key!=="Tab")return;f.preventDefault();let l=i.selectionStart,p=i.selectionEnd,h=i.value;i.value=`${h.slice(0,l)} ${h.slice(p)}`,i.selectionStart=i.selectionEnd=l+2,o(i.value),a(),e.onInput(i.value)});let s=null;return{el:t,getValue:()=>i.value,setValue:f=>{i.value=f,o(f),a()},setDisabled:(f,l)=>{i.disabled=f,f&&l?(s||(s=document.createElement("div"),s.className="dtk-ui-sqled-note",t.appendChild(s)),s.textContent=l,s.hidden=!1):s&&(s.hidden=!0)},focus:()=>i.focus()}}function $(e,t){let n=document.createElement(e);return t&&(n.className=t),n}function j(e,t,n){let r=$("div","dtk-ui-field"),i=$("span","dtk-ui-field-label");if(i.textContent=e,r.append(i,t),n){let o=$("div","dtk-ui-editor-hint");o.textContent=n,r.appendChild(o)}return r}function xe(e){let t=$("div","dtk-ui-form-sec"),n=$("div","dtk-ui-form-sec-head"),r=$("span","dtk-ui-form-sec-title");r.textContent=e,n.appendChild(r),t.appendChild(n);let i=$("div","dtk-ui-form-sec-body");return t.appendChild(i),{root:t,body:i}}function Ut(e){let t=document.createElement("details");t.className="dtk-ui-form-adv";let n=document.createElement("summary");n.className="dtk-ui-form-adv-summary",n.textContent=e,t.appendChild(n);let r=$("div","dtk-ui-form-adv-body");return t.appendChild(r),{details:t,body:r}}function Me(e,t,n){let r=$("div","dtk-ui-chipinput"),i=$("div","dtk-ui-chipinput-chips"),o=document.createElement("input");o.type="text",o.className="dtk-ui-chipinput-field",o.placeholder=t,r.append(i,o);let a=[...e];function s(){i.innerHTML="";for(let l of a){let p=$("span","dtk-ui-chip"),h=document.createElement("span");h.textContent=l;let m=document.createElement("button");m.type="button",m.className="dtk-ui-chip-x",m.textContent="\u2715",m.title=`remove ${l}`,m.onclick=()=>{a=a.filter(c=>c!==l),s(),n()},p.append(h,m),i.appendChild(p)}}s();function f(){let l=o.value.trim();o.value="",!(!l||a.includes(l))&&(a.push(l),s(),n())}return o.addEventListener("keydown",l=>{l.key==="Enter"&&(l.preventDefault(),f())}),o.addEventListener("blur",f),{el:r,get:()=>[...a],set:l=>{a=[...l],s()}}}function je(e){let t=e.trim();return/^\d+$/.test(t)?Number(t):t}var xn=new Set(["name","description","tags","profile","ai_context","query","query_file","query_columns","interval","loading_start_time","loading_delay","loading_batch_size","seasonality_columns","detectors","alerting","enabled"]);function wn(e){let t={};if(!e)return t;for(let[n,r]of Object.entries(e))xn.has(n)||(t[n]=r);return t}function En(e){let{root:t,body:n}=xe("Preserved fields"),r=$("div","dtk-ui-editor-hint");r.textContent="These config keys aren't editable in the Builder \u2014 they round-trip untouched. Use the YAML tab to change them.",n.appendChild(r);let i=$("div","dtk-ui-preschips");for(let o of Object.keys(e)){let a=$("span","dtk-ui-preschip");a.textContent=o,i.appendChild(a)}return n.appendChild(i),t}var zt=/^[A-Za-z0-9_-]+$/;function Cn(e,t,n){let{root:r,body:i}=xe("Basics"),o=document.createElement("input");o.type="text",o.className="dtk-ui-input",o.placeholder="my_metric",o.value=typeof(e==null?void 0:e.name)=="string"?e.name:"";let a=$("div","dtk-ui-form-err-inline");function s(){let x=o.value.trim();a.textContent=x===""||zt.test(x)?"":"letters, digits, _ and - only"}o.oninput=()=>{s(),n.onDirty()},s();let f=j("Name",o);f.appendChild(a),i.appendChild(f);let l=document.createElement("textarea");l.className="dtk-ui-input dtk-ui-textarea",l.rows=3,l.value=typeof(e==null?void 0:e.description)=="string"?e.description:"",l.oninput=()=>n.onDirty(),i.appendChild(j("Description",l));let p=Array.isArray(e==null?void 0:e.tags)?e.tags.map(String):[],h=Me(p,"add tag, Enter",()=>n.onDirty());i.appendChild(j("Tags",h.el));let m=document.createElement("select");m.className="dtk-ui-select";let c=document.createElement("option");c.value="",c.textContent="(project default)",m.appendChild(c);let v=typeof(e==null?void 0:e.profile)=="string"?e.profile:"",w=v&&!t.profiles.includes(v)?[v,...t.profiles]:t.profiles;for(let x of w){let b=document.createElement("option");b.value=x,b.textContent=x,m.appendChild(b)}m.value=v,m.onchange=()=>n.onDirty(),i.appendChild(j("Profile",m));let M=document.createElement("label");M.className="dtk-ui-check";let u=document.createElement("input");return u.type="checkbox",u.checked=(e==null?void 0:e.enabled)!==!1,u.onchange=()=>n.onDirty(),M.append(u,document.createTextNode("Enabled")),i.appendChild(M),{el:r,getName:()=>o.value.trim(),getDescription:()=>l.value,getTags:()=>h.get(),getEnabled:()=>u.checked,getProfile:()=>m.value.trim(),setName:x=>{o.value=x,s()},setDescription:x=>{l.value=x},focusName:()=>o.focus(),issues:()=>{let x=o.value.trim();return x===""?["name is required"]:zt.test(x)?[]:["name has invalid characters (use letters, digits, _ or -)"]}}}var Sn=["10min","1h","1d"],Mn=/^\d+(s|sec|secs|second|seconds|min|mins|minute|minutes|h|hr|hour|hours|d|day|days)?$/i;function Tn(e,t){let{root:n,body:r}=xe("Schedule & loading"),i=document.createElement("input");i.type="text",i.className="dtk-ui-input",i.placeholder="1h",i.value=(e==null?void 0:e.interval)!==void 0&&(e==null?void 0:e.interval)!==null?String(e.interval):"1h";let o=$("div","dtk-ui-form-err-inline");function a(){let g=i.value.trim();o.textContent=g===""||Mn.test(g)?"":"looks unusual \u2014 the server validates on save"}i.oninput=()=>{a(),t.onDirty()},a();let s=j("Interval",i),f=$("div","dtk-ui-chip-presets");for(let g of Sn){let S=document.createElement("button");S.type="button",S.className="dtk-ui-chip-preset",S.textContent=g,S.onclick=()=>{i.value=g,a(),t.onDirty()},f.appendChild(S)}s.appendChild(f),s.appendChild(o),r.appendChild(s);let l=document.createElement("input");l.type="text",l.className="dtk-ui-input",l.placeholder="YYYY-MM-DD HH:MM:SS",l.value=typeof(e==null?void 0:e.loading_start_time)=="string"?e.loading_start_time:"",l.oninput=()=>t.onDirty(),r.appendChild(j("Loading start time",l));let p=e!=null&&e.query_columns&&typeof e.query_columns=="object"?e.query_columns:{},h=typeof(e==null?void 0:e.loading_batch_size)=="number"?e.loading_batch_size:null,m=(e==null?void 0:e.loading_delay)!==void 0||h!==null&&h!==1e4||Object.keys(p).length>0,c=Ut("Advanced");c.details.open=m,r.appendChild(c.details);let v=document.createElement("input");v.type="text",v.className="dtk-ui-input",v.placeholder="e.g. 10min (optional)",v.value=(e==null?void 0:e.loading_delay)!==void 0&&(e==null?void 0:e.loading_delay)!==null?String(e.loading_delay):"",v.oninput=()=>t.onDirty(),c.body.appendChild(j("Loading delay",v,"Data-maturity delay before the newest interval is trusted complete."));let w=document.createElement("input");w.type="number",w.min="1",w.className="dtk-ui-input",w.placeholder="10000",w.value=h!==null&&h!==1e4?String(h):"",w.oninput=()=>t.onDirty(),c.body.appendChild(j("Loading batch size",w));let M=document.createElement("input");M.type="text",M.className="dtk-ui-input",M.placeholder="timestamp",M.value=typeof p.timestamp=="string"?p.timestamp:"",M.oninput=()=>t.onDirty();let u=document.createElement("input");u.type="text",u.className="dtk-ui-input",u.placeholder="value",u.value=typeof p.metric=="string"?p.metric:"",u.oninput=()=>t.onDirty();let x=$("div","dtk-ui-row2");x.append(j("Timestamp column",M),j("Value column",u)),c.body.appendChild(x);let b=Array.isArray(p.seasonality)?p.seasonality.map(String):[],k=Me(b,"add query column name, Enter",()=>t.onDirty());return c.body.appendChild(j("Seasonality columns (in query results)",k.el)),{el:n,getInterval:()=>i.value.trim(),getLoadingStartTime:()=>l.value.trim(),getLoadingDelay:()=>v.value.trim(),getLoadingBatchSize:()=>{let g=w.value.trim();if(!g)return null;let S=Math.round(Number(g));return Number.isFinite(S)&&S!==1e4?S:null},getQueryColumns:()=>{let g=M.value.trim(),S=u.value.trim(),N=k.get();if(!g&&!S&&N.length===0)return null;let L={};return g&&(L.timestamp=g),S&&(L.metric=S),N.length>0&&(L.seasonality=N),L},issues:()=>i.value.trim()===""?["interval is required"]:[]}}var Xe=[{key:"hour",label:"Hour"},{key:"day_of_week",label:"Day of week"},{key:"day_of_month",label:"Day of month"},{key:"month",label:"Month"},{key:"is_weekend",label:"Weekend"},{key:"is_holiday",label:"Holiday"}];function Nn(e,t){let{root:n,body:r}=xe("Seasonality"),i=new Set(Array.isArray(e==null?void 0:e.seasonality_columns)?e.seasonality_columns.map(String):[]),o={},a=$("div","dtk-ui-checks");for(let{key:s,label:f}of Xe){let l=document.createElement("label");l.className="dtk-ui-check";let p=document.createElement("input");p.type="checkbox",p.checked=i.has(s),p.onchange=()=>t.onDirty(),l.append(p,document.createTextNode(f)),a.appendChild(l),o[s]=p}return r.appendChild(a),{el:n,getColumns:()=>Xe.filter(s=>o[s.key].checked).map(s=>s.key),setColumns:s=>{let f=new Set(s);for(let{key:l}of Xe)o[l].checked=f.has(l)}}}var Yt=["mad","zscore","iqr","autoreg","manual_bounds"];function _e(e){return Yt.includes(e)}var De={mad:["threshold","window_size"],zscore:["threshold","window_size"],iqr:["threshold","window_size"],autoreg:["threshold","window_size","lags"],manual_bounds:["lower_bound","upper_bound"]},Rn={threshold:"Threshold",window_size:"Window size",lags:"Lags",lower_bound:"Lower bound",upper_bound:"Upper bound"},Ln={mad:{threshold:"3.0",window_size:"100"},zscore:{threshold:"3.0",window_size:"100"},iqr:{threshold:"3.0",window_size:"100"},autoreg:{threshold:"3.0",window_size:"200",lags:"5"},manual_bounds:{}};function Ze(e){let t=e&&typeof e=="object"?{...e}:{},n=typeof t.type=="string"?t.type:"";if(!_e(n))return{type:n,raw:t,extraParams:{},extrasOwner:n,fields:{}};let r=t.params&&typeof t.params=="object"?t.params:{},i=new Set(De[n]),o={};for(let[s,f]of Object.entries(r))i.has(s)||(o[s]=f);let a={};for(let s of De[n]){let f=r[s];f!=null&&(a[s]=String(f))}return{type:n,raw:t,extraParams:o,extrasOwner:n,fields:a}}function _n(e){if(!_e(e.type))return e.raw;let t=De[e.type],n=new Set(t),r={};if(e.type===e.extrasOwner)for(let[i,o]of Object.entries(e.extraParams))n.has(i)||(r[i]=o);for(let i of t){let o=e.fields[i];if(o===void 0||o.trim()==="")continue;let a=Number(o);r[i]=Number.isFinite(a)?a:o}return{type:e.type,params:r}}function Dn(e,t,n){let{root:r,body:i}=xe("Detectors"),o=$("div","dtk-ui-editor-hint");o.textContent="Defaults are a fine start \u2014 load data, then fine-tune in the tune cockpit (Tune button on the metric row).",i.appendChild(o);let a=$("div","dtk-ui-detrows");i.appendChild(a);let s=document.createElement("button");s.type="button",s.className="dtk-ui-btn",s.textContent="+ Add detector",i.appendChild(s);let f=Array.isArray(e==null?void 0:e.detectors)?e.detectors:[],l=f.length>0?f.map(Ze):n==="create"?[Ze({type:"mad",params:{threshold:3,window_size:100}})]:[];function p(m){var M,u,x;let c=$("div","dtk-ui-detrow"),v=$("div","dtk-ui-detrow-head");if(_e(m.type)){let b=document.createElement("select");b.className="dtk-ui-select";for(let k of Yt){let g=document.createElement("option");g.value=k,g.textContent=k,b.appendChild(g)}b.value=m.type,b.onchange=()=>{let k=b.value;if(!_e(k))return;let g={};for(let S of De[k])m.fields[S]!==void 0&&(g[S]=m.fields[S]);m.type=k,m.fields=g,t.onDirty(),h()},v.appendChild(b)}else{let b=$("span","dtk-ui-detrow-readonly");b.textContent=`${m.type||"unknown type"} \u2014 preserved as-is, edit in the YAML tab`,v.appendChild(b)}let w=document.createElement("button");if(w.type="button",w.className="dtk-ui-detrow-remove",w.textContent="\u2715",w.title="Remove detector",w.onclick=()=>{l=l.filter(b=>b!==m),t.onDirty(),h()},v.appendChild(w),c.appendChild(v),_e(m.type)){let b=$("div","dtk-ui-detrow-fields"),k=De[m.type];for(let N of k){let L=document.createElement("input");L.type="number",L.step="any",L.className="dtk-ui-input",L.placeholder=(M=Ln[m.type][N])!=null?M:"",L.value=(u=m.fields[N])!=null?u:"",L.oninput=()=>{m.fields[N]=L.value,t.onDirty()},b.appendChild(j((x=Rn[N])!=null?x:N,L))}c.appendChild(b);let g=new Set(k),S=Object.keys(m.extraParams).filter(N=>!g.has(N)).length;if(S>0){let N=$("span","dtk-ui-preschip");N.textContent=m.type===m.extrasOwner?`+${S} params preserved`:`${S} ${m.extrasOwner} param(s) withheld \u2014 switch the type back to restore`,c.appendChild(N)}}return c}function h(){a.innerHTML="";for(let m of l)a.appendChild(p(m))}return h(),s.onclick=()=>{l.push(Ze({type:"mad",params:{threshold:3,window_size:100}})),t.onDirty(),h()},{el:r,getData:()=>l.map(_n),issues:()=>{let m=[];for(let c of l)if(c.type==="manual_bounds"){let v=c.fields.lower_bound,w=c.fields.upper_bound;(!v||v.trim()==="")&&(!w||w.trim()==="")&&m.push("a manual_bounds detector needs at least a lower or upper bound")}return m}}}var Bt=new Set(["enabled","channels","consecutive_anomalies","direction","no_data_alert","notify_on_recovery","alert_cooldown","min_detectors","anomaly_window","min_anomaly_share","mentions","dashboard_url"]);function $n(e){let t=e==null?void 0:e.alerting;return t==null?{mode:"none",block:{}}:Array.isArray(t)?t.length>1?{mode:"verbatim",block:{},verbatim:t}:t.length===1&&t[0]&&typeof t[0]=="object"?{mode:"single",block:t[0]}:{mode:"none",block:{}}:typeof t=="object"?{mode:"single",block:t}:{mode:"none",block:{}}}function On(e,t,n){let{root:r,body:i}=xe("Alerting"),o=$n(e);if(o.mode==="verbatim"){let E=$("div","dtk-ui-form-warn");return E.textContent="Multiple alerting configs \u2014 edit them in the YAML tab.",i.appendChild(E),{el:r,getData:()=>o.verbatim,issues:()=>[]}}let a=o.block,s={};for(let[E,T]of Object.entries(a))Bt.has(E)||(s[E]=T);let f=document.createElement("label");f.className="dtk-ui-check";let l=document.createElement("input");l.type="checkbox",l.checked=o.mode==="single",f.append(l,document.createTextNode("Alerting")),i.appendChild(f);let p=$("div","dtk-ui-alerting-fields");i.appendChild(p);let h=document.createElement("label");h.className="dtk-ui-check";let m=document.createElement("input");m.type="checkbox",m.checked=a.enabled!==!1,m.onchange=()=>n.onDirty(),h.append(m,document.createTextNode("Enabled (uncheck to keep the config but pause sending)")),p.appendChild(h);let c=new Set(t.channels.map(E=>E.name)),v=Array.isArray(a.channels)?a.channels.map(String):[],w=new Set(v.filter(E=>c.has(E))),M=v.filter(E=>!c.has(E)),u=$("div","dtk-ui-field"),x=$("span","dtk-ui-field-label");x.textContent="Channels",u.appendChild(x);let b=$("div","dtk-ui-checks");for(let E of t.channels){let T=document.createElement("label");T.className="dtk-ui-check";let Y=document.createElement("input");Y.type="checkbox",Y.checked=w.has(E.name),Y.onchange=()=>{Y.checked?w.add(E.name):w.delete(E.name),n.onDirty()};let W=$("span","dtk-ui-check-type");W.textContent=`(${E.type})`,T.append(Y,document.createTextNode(E.name+" "),W),b.appendChild(T)}u.appendChild(b);let k=Me(M,"add channel name, Enter",()=>n.onDirty());u.appendChild(k.el),p.appendChild(u);let g=document.createElement("input");g.type="number",g.min="1",g.step="1",g.className="dtk-ui-input",g.value=String(typeof a.consecutive_anomalies=="number"?a.consecutive_anomalies:3),g.oninput=()=>n.onDirty(),p.appendChild(j("Consecutive anomalies",g));let S=document.createElement("select");S.className="dtk-ui-select";for(let E of["same","any","up","down"]){let T=document.createElement("option");T.value=E,T.textContent=E,S.appendChild(T)}S.value=typeof a.direction=="string"?a.direction:"same",S.onchange=()=>n.onDirty(),p.appendChild(j("Direction",S));let N=document.createElement("label");N.className="dtk-ui-check";let L=document.createElement("input");L.type="checkbox",L.checked=a.no_data_alert===!0,L.onchange=()=>n.onDirty(),N.append(L,document.createTextNode("Alert on missing data")),p.appendChild(N);let _=document.createElement("label");_.className="dtk-ui-check";let A=document.createElement("input");A.type="checkbox",A.checked=a.notify_on_recovery===!0,A.onchange=()=>n.onDirty(),_.append(A,document.createTextNode("Notify on recovery")),p.appendChild(_);let I=document.createElement("input");I.type="text",I.className="dtk-ui-input",I.placeholder="e.g. 30min (optional)",I.value=a.alert_cooldown!==void 0&&a.alert_cooldown!==null?String(a.alert_cooldown):"",I.oninput=()=>n.onDirty(),p.appendChild(j("Alert cooldown",I));let z=a.min_detectors!==void 0||a.anomaly_window!==void 0||a.min_anomaly_share!==void 0||Array.isArray(a.mentions)&&a.mentions.length>0||!!a.dashboard_url,K=Ut("Advanced");K.details.open=z,p.appendChild(K.details);let q=document.createElement("input");q.type="number",q.min="1",q.step="1",q.className="dtk-ui-input",q.value=String(typeof a.min_detectors=="number"?a.min_detectors:1),q.oninput=()=>n.onDirty(),K.body.appendChild(j("Min detectors",q));let G=$("div","dtk-ui-row2"),U=document.createElement("input");U.type="text",U.className="dtk-ui-input",U.placeholder="e.g. 30min",U.value=a.anomaly_window!==void 0&&a.anomaly_window!==null?String(a.anomaly_window):"",U.oninput=()=>n.onDirty();let B=document.createElement("input");B.type="number",B.min="0",B.max="1",B.step="0.05",B.className="dtk-ui-input",B.placeholder="e.g. 0.3",B.value=typeof a.min_anomaly_share=="number"?String(a.min_anomaly_share):"",B.oninput=()=>n.onDirty(),G.append(j("Anomaly window",U),j("Min anomaly share",B)),K.body.appendChild(G);let le=Array.isArray(a.mentions)?a.mentions.map(String):[],ne=Me(le,"add mention, Enter",()=>n.onDirty());K.body.appendChild(j("Mentions",ne.el));let J=document.createElement("input");if(J.type="text",J.className="dtk-ui-input",J.placeholder="https://\u2026",J.value=typeof a.dashboard_url=="string"?a.dashboard_url:"",J.oninput=()=>n.onDirty(),K.body.appendChild(j("Dashboard URL",J)),Object.keys(s).length>0){let E=$("span","dtk-ui-preschip");E.textContent=`+${Object.keys(s).length} fields preserved`,p.appendChild(E)}function V(){p.style.display=l.checked?"":"none"}return l.onchange=()=>{V(),n.onDirty()},V(),{el:r,getData:()=>{if(!l.checked)return;let E={enabled:m.checked},T=[...t.channels.filter(F=>w.has(F.name)).map(F=>F.name),...k.get()];T.length>0&&(E.channels=T);let Y=Number(g.value);E.consecutive_anomalies=g.value.trim()!==""&&Number.isFinite(Y)?Math.max(1,Math.round(Y)):3,E.direction=S.value,E.no_data_alert=L.checked,E.notify_on_recovery=A.checked;let W=I.value.trim();W&&(E.alert_cooldown=je(W));let Q=Number(q.value),oe=q.value.trim()!==""&&Number.isFinite(Q)?Math.max(1,Math.round(Q)):1;oe!==1&&(E.min_detectors=oe);let me=U.value.trim(),D=B.value.trim();me&&D&&(E.anomaly_window=je(me),E.min_anomaly_share=Number(D));let X=ne.get();X.length>0&&(E.mentions=X);let ie=J.value.trim();ie&&(E.dashboard_url=ie);for(let[F,ee]of Object.entries(s))Bt.has(F)||(E[F]=ee);return E},issues:()=>{if(!l.checked)return[];let E=U.value.trim(),T=B.value.trim();return E===""!=(T==="")?["anomaly window and min anomaly share must be set together"]:[]}}}function Jt(e){if(typeof e=="string")return{instructions:e,synonyms:[],examples:[]};if(e&&typeof e=="object"){let t=e;return{instructions:typeof t.instructions=="string"?t.instructions:"",synonyms:Array.isArray(t.synonyms)?t.synonyms.map(String):[],examples:Array.isArray(t.examples)?t.examples.map(String):[]}}return{instructions:"",synonyms:[],examples:[]}}function In(e,t){let n=Jt(e==null?void 0:e.ai_context),{root:r,body:i}=xe("AI context"),o=document.createElement("details");o.className="dtk-ui-form-adv",o.open=!!(n.instructions||n.synonyms.length>0||n.examples.length>0);let a=document.createElement("summary");a.className="dtk-ui-form-adv-summary",a.textContent="Instructions / synonyms / examples",o.appendChild(a);let s=$("div","dtk-ui-form-adv-body");o.appendChild(s),i.appendChild(o);let f=document.createElement("textarea");f.className="dtk-ui-input dtk-ui-textarea",f.rows=3,f.placeholder="Business meaning / how to interpret this metric",f.value=n.instructions,f.oninput=()=>t.onDirty(),s.appendChild(j("Instructions",f));let l=Me(n.synonyms,"add synonym, Enter",()=>t.onDirty());s.appendChild(j("Synonyms",l.el));let p=Me(n.examples,"add example, Enter",()=>t.onDirty());return s.appendChild(j("Examples",p.el)),{el:r,getData:()=>{let h=f.value.trim(),m=l.get(),c=p.get();if(!h&&m.length===0&&c.length===0)return;let v={};return h&&(v.instructions=h),m.length>0&&(v.synonyms=m),c.length>0&&(v.examples=c),v},setData:h=>{let m=Jt(h!=null?h:null);f.value=m.instructions,l.set(m.synonyms),p.set(m.examples),(m.instructions||m.synonyms.length>0||m.examples.length>0)&&(o.open=!0)}}}var An=`SELECT
471
+ `}function Ft(e){let t=document.createElement("div");t.className="dtk-ui-sqled";let n=document.createElement("pre");n.className="dtk-ui-sqled-hl",n.setAttribute("aria-hidden","true");let r=document.createElement("code");n.appendChild(r);let i=document.createElement("textarea");i.className="dtk-ui-sqled-ta",i.spellcheck=!1,i.autocapitalize="off",e.ariaLabel&&i.setAttribute("aria-label",e.ariaLabel),t.append(n,i);function o(f){r.innerHTML=kn(f)}function a(){n.scrollTop=i.scrollTop,n.scrollLeft=i.scrollLeft}i.value=e.value,o(e.value),i.addEventListener("input",()=>{o(i.value),a(),e.onInput(i.value)}),i.addEventListener("scroll",a),i.addEventListener("keydown",f=>{if(f.key!=="Tab")return;f.preventDefault();let l=i.selectionStart,p=i.selectionEnd,h=i.value;i.value=`${h.slice(0,l)} ${h.slice(p)}`,i.selectionStart=i.selectionEnd=l+2,o(i.value),a(),e.onInput(i.value)});let s=null;return{el:t,getValue:()=>i.value,setValue:f=>{i.value=f,o(f),a()},setDisabled:(f,l)=>{i.disabled=f,f&&l?(s||(s=document.createElement("div"),s.className="dtk-ui-sqled-note",t.appendChild(s)),s.textContent=l,s.hidden=!1):s&&(s.hidden=!0)},focus:()=>i.focus()}}function $(e,t){let n=document.createElement(e);return t&&(n.className=t),n}function j(e,t,n){let r=$("div","dtk-ui-field"),i=$("span","dtk-ui-field-label");if(i.textContent=e,r.append(i,t),n){let o=$("div","dtk-ui-editor-hint");o.textContent=n,r.appendChild(o)}return r}function xe(e){let t=$("div","dtk-ui-form-sec"),n=$("div","dtk-ui-form-sec-head"),r=$("span","dtk-ui-form-sec-title");r.textContent=e,n.appendChild(r),t.appendChild(n);let i=$("div","dtk-ui-form-sec-body");return t.appendChild(i),{root:t,body:i}}function Ut(e){let t=document.createElement("details");t.className="dtk-ui-form-adv";let n=document.createElement("summary");n.className="dtk-ui-form-adv-summary",n.textContent=e,t.appendChild(n);let r=$("div","dtk-ui-form-adv-body");return t.appendChild(r),{details:t,body:r}}function Me(e,t,n){let r=$("div","dtk-ui-chipinput"),i=$("div","dtk-ui-chipinput-chips"),o=document.createElement("input");o.type="text",o.className="dtk-ui-chipinput-field",o.placeholder=t,r.append(i,o);let a=[...e];function s(){i.innerHTML="";for(let l of a){let p=$("span","dtk-ui-chip"),h=document.createElement("span");h.textContent=l;let m=document.createElement("button");m.type="button",m.className="dtk-ui-chip-x",m.textContent="\u2715",m.title=`remove ${l}`,m.onclick=()=>{a=a.filter(c=>c!==l),s(),n()},p.append(h,m),i.appendChild(p)}}s();function f(){let l=o.value.trim();o.value="",!(!l||a.includes(l))&&(a.push(l),s(),n())}return o.addEventListener("keydown",l=>{l.key==="Enter"&&(l.preventDefault(),f())}),o.addEventListener("blur",f),{el:r,get:()=>[...a],set:l=>{a=[...l],s()}}}function je(e){let t=e.trim();return/^\d+$/.test(t)?Number(t):t}var xn=new Set(["name","description","tags","profile","ai_context","query","query_file","query_columns","interval","loading_start_time","loading_delay","loading_batch_size","seasonality_columns","detectors","alerting","enabled"]);function wn(e){let t={};if(!e)return t;for(let[n,r]of Object.entries(e))xn.has(n)||(t[n]=r);return t}function En(e){let{root:t,body:n}=xe("Preserved fields"),r=$("div","dtk-ui-editor-hint");r.textContent="These config keys aren't editable in the Builder \u2014 they round-trip untouched. Use the YAML tab to change them.",n.appendChild(r);let i=$("div","dtk-ui-preschips");for(let o of Object.keys(e)){let a=$("span","dtk-ui-preschip");a.textContent=o,i.appendChild(a)}return n.appendChild(i),t}var zt=/^[A-Za-z0-9_-]+$/;function Cn(e,t,n){let{root:r,body:i}=xe("Basics"),o=document.createElement("input");o.type="text",o.className="dtk-ui-input",o.placeholder="my_metric",o.value=typeof(e==null?void 0:e.name)=="string"?e.name:e===null?"my_metric":"";let a=$("div","dtk-ui-form-err-inline");function s(){let x=o.value.trim();a.textContent=x===""||zt.test(x)?"":"letters, digits, _ and - only"}o.oninput=()=>{s(),n.onDirty()},s();let f=j("Name",o);f.appendChild(a),i.appendChild(f);let l=document.createElement("textarea");l.className="dtk-ui-input dtk-ui-textarea",l.rows=3,l.value=typeof(e==null?void 0:e.description)=="string"?e.description:"",l.oninput=()=>n.onDirty(),i.appendChild(j("Description",l));let p=Array.isArray(e==null?void 0:e.tags)?e.tags.map(String):[],h=Me(p,"add tag, Enter",()=>n.onDirty());i.appendChild(j("Tags",h.el));let m=document.createElement("select");m.className="dtk-ui-select";let c=document.createElement("option");c.value="",c.textContent="(project default)",m.appendChild(c);let v=typeof(e==null?void 0:e.profile)=="string"?e.profile:"",w=v&&!t.profiles.includes(v)?[v,...t.profiles]:t.profiles;for(let x of w){let b=document.createElement("option");b.value=x,b.textContent=x,m.appendChild(b)}m.value=v,m.onchange=()=>n.onDirty(),i.appendChild(j("Profile",m));let M=document.createElement("label");M.className="dtk-ui-check";let u=document.createElement("input");return u.type="checkbox",u.checked=(e==null?void 0:e.enabled)!==!1,u.onchange=()=>n.onDirty(),M.append(u,document.createTextNode("Enabled")),i.appendChild(M),{el:r,getName:()=>o.value.trim(),getDescription:()=>l.value,getTags:()=>h.get(),getEnabled:()=>u.checked,getProfile:()=>m.value.trim(),setName:x=>{o.value=x,s()},setDescription:x=>{l.value=x},focusName:()=>o.focus(),issues:()=>{let x=o.value.trim();return x===""?["name is required"]:zt.test(x)?[]:["name has invalid characters (use letters, digits, _ or -)"]}}}var Sn=["10min","1h","1d"],Mn=/^\d+(s|sec|secs|second|seconds|min|mins|minute|minutes|h|hr|hour|hours|d|day|days)?$/i;function Tn(e,t){let{root:n,body:r}=xe("Schedule & loading"),i=document.createElement("input");i.type="text",i.className="dtk-ui-input",i.placeholder="1h",i.value=(e==null?void 0:e.interval)!==void 0&&(e==null?void 0:e.interval)!==null?String(e.interval):"1h";let o=$("div","dtk-ui-form-err-inline");function a(){let g=i.value.trim();o.textContent=g===""||Mn.test(g)?"":"looks unusual \u2014 the server validates on save"}i.oninput=()=>{a(),t.onDirty()},a();let s=j("Interval",i),f=$("div","dtk-ui-chip-presets");for(let g of Sn){let S=document.createElement("button");S.type="button",S.className="dtk-ui-chip-preset",S.textContent=g,S.onclick=()=>{i.value=g,a(),t.onDirty()},f.appendChild(S)}s.appendChild(f),s.appendChild(o),r.appendChild(s);let l=document.createElement("input");l.type="text",l.className="dtk-ui-input",l.placeholder="YYYY-MM-DD HH:MM:SS",l.value=typeof(e==null?void 0:e.loading_start_time)=="string"?e.loading_start_time:"",l.oninput=()=>t.onDirty(),r.appendChild(j("Loading start time",l));let p=e!=null&&e.query_columns&&typeof e.query_columns=="object"?e.query_columns:{},h=typeof(e==null?void 0:e.loading_batch_size)=="number"?e.loading_batch_size:null,m=(e==null?void 0:e.loading_delay)!==void 0||h!==null&&h!==1e4||Object.keys(p).length>0,c=Ut("Advanced");c.details.open=m,r.appendChild(c.details);let v=document.createElement("input");v.type="text",v.className="dtk-ui-input",v.placeholder="e.g. 10min (optional)",v.value=(e==null?void 0:e.loading_delay)!==void 0&&(e==null?void 0:e.loading_delay)!==null?String(e.loading_delay):"",v.oninput=()=>t.onDirty(),c.body.appendChild(j("Loading delay",v,"Data-maturity delay before the newest interval is trusted complete."));let w=document.createElement("input");w.type="number",w.min="1",w.className="dtk-ui-input",w.placeholder="10000",w.value=h!==null&&h!==1e4?String(h):"",w.oninput=()=>t.onDirty(),c.body.appendChild(j("Loading batch size",w));let M=document.createElement("input");M.type="text",M.className="dtk-ui-input",M.placeholder="timestamp",M.value=typeof p.timestamp=="string"?p.timestamp:"",M.oninput=()=>t.onDirty();let u=document.createElement("input");u.type="text",u.className="dtk-ui-input",u.placeholder="value",u.value=typeof p.metric=="string"?p.metric:"",u.oninput=()=>t.onDirty();let x=$("div","dtk-ui-row2");x.append(j("Timestamp column",M),j("Value column",u)),c.body.appendChild(x);let b=Array.isArray(p.seasonality)?p.seasonality.map(String):[],k=Me(b,"add query column name, Enter",()=>t.onDirty());return c.body.appendChild(j("Seasonality columns (in query results)",k.el)),{el:n,getInterval:()=>i.value.trim(),getLoadingStartTime:()=>l.value.trim(),getLoadingDelay:()=>v.value.trim(),getLoadingBatchSize:()=>{let g=w.value.trim();if(!g)return null;let S=Math.round(Number(g));return Number.isFinite(S)&&S!==1e4?S:null},getQueryColumns:()=>{let g=M.value.trim(),S=u.value.trim(),N=k.get();if(!g&&!S&&N.length===0)return null;let L={};return g&&(L.timestamp=g),S&&(L.metric=S),N.length>0&&(L.seasonality=N),L},issues:()=>i.value.trim()===""?["interval is required"]:[]}}var Xe=[{key:"hour",label:"Hour"},{key:"day_of_week",label:"Day of week"},{key:"day_of_month",label:"Day of month"},{key:"month",label:"Month"},{key:"is_weekend",label:"Weekend"},{key:"is_holiday",label:"Holiday"}];function Nn(e,t){let{root:n,body:r}=xe("Seasonality"),i=new Set(Array.isArray(e==null?void 0:e.seasonality_columns)?e.seasonality_columns.map(String):[]),o={},a=$("div","dtk-ui-checks");for(let{key:s,label:f}of Xe){let l=document.createElement("label");l.className="dtk-ui-check";let p=document.createElement("input");p.type="checkbox",p.checked=i.has(s),p.onchange=()=>t.onDirty(),l.append(p,document.createTextNode(f)),a.appendChild(l),o[s]=p}return r.appendChild(a),{el:n,getColumns:()=>Xe.filter(s=>o[s.key].checked).map(s=>s.key),setColumns:s=>{let f=new Set(s);for(let{key:l}of Xe)o[l].checked=f.has(l)}}}var Yt=["mad","zscore","iqr","autoreg","manual_bounds"];function _e(e){return Yt.includes(e)}var De={mad:["threshold","window_size"],zscore:["threshold","window_size"],iqr:["threshold","window_size"],autoreg:["threshold","window_size","lags"],manual_bounds:["lower_bound","upper_bound"]},Rn={threshold:"Threshold",window_size:"Window size",lags:"Lags",lower_bound:"Lower bound",upper_bound:"Upper bound"},Ln={mad:{threshold:"3.0",window_size:"100"},zscore:{threshold:"3.0",window_size:"100"},iqr:{threshold:"3.0",window_size:"100"},autoreg:{threshold:"3.0",window_size:"200",lags:"5"},manual_bounds:{}};function Ze(e){let t=e&&typeof e=="object"?{...e}:{},n=typeof t.type=="string"?t.type:"";if(!_e(n))return{type:n,raw:t,extraParams:{},extrasOwner:n,fields:{}};let r=t.params&&typeof t.params=="object"?t.params:{},i=new Set(De[n]),o={};for(let[s,f]of Object.entries(r))i.has(s)||(o[s]=f);let a={};for(let s of De[n]){let f=r[s];f!=null&&(a[s]=String(f))}return{type:n,raw:t,extraParams:o,extrasOwner:n,fields:a}}function _n(e){if(!_e(e.type))return e.raw;let t=De[e.type],n=new Set(t),r={};if(e.type===e.extrasOwner)for(let[i,o]of Object.entries(e.extraParams))n.has(i)||(r[i]=o);for(let i of t){let o=e.fields[i];if(o===void 0||o.trim()==="")continue;let a=Number(o);r[i]=Number.isFinite(a)?a:o}return{type:e.type,params:r}}function Dn(e,t,n){let{root:r,body:i}=xe("Detectors"),o=$("div","dtk-ui-editor-hint");o.textContent="Defaults are a fine start \u2014 load data, then fine-tune in the tune cockpit (Tune button on the metric row).",i.appendChild(o);let a=$("div","dtk-ui-detrows");i.appendChild(a);let s=document.createElement("button");s.type="button",s.className="dtk-ui-btn",s.textContent="+ Add detector",i.appendChild(s);let f=Array.isArray(e==null?void 0:e.detectors)?e.detectors:[],l=f.length>0?f.map(Ze):n==="create"?[Ze({type:"mad",params:{threshold:3,window_size:100}})]:[];function p(m){var M,u,x;let c=$("div","dtk-ui-detrow"),v=$("div","dtk-ui-detrow-head");if(_e(m.type)){let b=document.createElement("select");b.className="dtk-ui-select";for(let k of Yt){let g=document.createElement("option");g.value=k,g.textContent=k,b.appendChild(g)}b.value=m.type,b.onchange=()=>{let k=b.value;if(!_e(k))return;let g={};for(let S of De[k])m.fields[S]!==void 0&&(g[S]=m.fields[S]);m.type=k,m.fields=g,t.onDirty(),h()},v.appendChild(b)}else{let b=$("span","dtk-ui-detrow-readonly");b.textContent=`${m.type||"unknown type"} \u2014 preserved as-is, edit in the YAML tab`,v.appendChild(b)}let w=document.createElement("button");if(w.type="button",w.className="dtk-ui-detrow-remove",w.textContent="\u2715",w.title="Remove detector",w.onclick=()=>{l=l.filter(b=>b!==m),t.onDirty(),h()},v.appendChild(w),c.appendChild(v),_e(m.type)){let b=$("div","dtk-ui-detrow-fields"),k=De[m.type];for(let N of k){let L=document.createElement("input");L.type="number",L.step="any",L.className="dtk-ui-input",L.placeholder=(M=Ln[m.type][N])!=null?M:"",L.value=(u=m.fields[N])!=null?u:"",L.oninput=()=>{m.fields[N]=L.value,t.onDirty()},b.appendChild(j((x=Rn[N])!=null?x:N,L))}c.appendChild(b);let g=new Set(k),S=Object.keys(m.extraParams).filter(N=>!g.has(N)).length;if(S>0){let N=$("span","dtk-ui-preschip");N.textContent=m.type===m.extrasOwner?`+${S} params preserved`:`${S} ${m.extrasOwner} param(s) withheld \u2014 switch the type back to restore`,c.appendChild(N)}}return c}function h(){a.innerHTML="";for(let m of l)a.appendChild(p(m))}return h(),s.onclick=()=>{l.push(Ze({type:"mad",params:{threshold:3,window_size:100}})),t.onDirty(),h()},{el:r,getData:()=>l.map(_n),issues:()=>{let m=[];for(let c of l)if(c.type==="manual_bounds"){let v=c.fields.lower_bound,w=c.fields.upper_bound;(!v||v.trim()==="")&&(!w||w.trim()==="")&&m.push("a manual_bounds detector needs at least a lower or upper bound")}return m}}}var Bt=new Set(["enabled","channels","consecutive_anomalies","direction","no_data_alert","notify_on_recovery","alert_cooldown","min_detectors","anomaly_window","min_anomaly_share","mentions","dashboard_url"]);function $n(e){let t=e==null?void 0:e.alerting;return t==null?{mode:"none",block:{}}:Array.isArray(t)?t.length>1?{mode:"verbatim",block:{},verbatim:t}:t.length===1&&t[0]&&typeof t[0]=="object"?{mode:"single",block:t[0]}:{mode:"none",block:{}}:typeof t=="object"?{mode:"single",block:t}:{mode:"none",block:{}}}function On(e,t,n){let{root:r,body:i}=xe("Alerting"),o=$n(e);if(o.mode==="verbatim"){let E=$("div","dtk-ui-form-warn");return E.textContent="Multiple alerting configs \u2014 edit them in the YAML tab.",i.appendChild(E),{el:r,getData:()=>o.verbatim,issues:()=>[]}}let a=o.block,s={};for(let[E,T]of Object.entries(a))Bt.has(E)||(s[E]=T);let f=document.createElement("label");f.className="dtk-ui-check";let l=document.createElement("input");l.type="checkbox",l.checked=o.mode==="single",f.append(l,document.createTextNode("Alerting")),i.appendChild(f);let p=$("div","dtk-ui-alerting-fields");i.appendChild(p);let h=document.createElement("label");h.className="dtk-ui-check";let m=document.createElement("input");m.type="checkbox",m.checked=a.enabled!==!1,m.onchange=()=>n.onDirty(),h.append(m,document.createTextNode("Enabled (uncheck to keep the config but pause sending)")),p.appendChild(h);let c=new Set(t.channels.map(E=>E.name)),v=Array.isArray(a.channels)?a.channels.map(String):[],w=new Set(v.filter(E=>c.has(E))),M=v.filter(E=>!c.has(E)),u=$("div","dtk-ui-field"),x=$("span","dtk-ui-field-label");x.textContent="Channels",u.appendChild(x);let b=$("div","dtk-ui-checks");for(let E of t.channels){let T=document.createElement("label");T.className="dtk-ui-check";let Y=document.createElement("input");Y.type="checkbox",Y.checked=w.has(E.name),Y.onchange=()=>{Y.checked?w.add(E.name):w.delete(E.name),n.onDirty()};let W=$("span","dtk-ui-check-type");W.textContent=`(${E.type})`,T.append(Y,document.createTextNode(E.name+" "),W),b.appendChild(T)}u.appendChild(b);let k=Me(M,"add channel name, Enter",()=>n.onDirty());u.appendChild(k.el),p.appendChild(u);let g=document.createElement("input");g.type="number",g.min="1",g.step="1",g.className="dtk-ui-input",g.value=String(typeof a.consecutive_anomalies=="number"?a.consecutive_anomalies:3),g.oninput=()=>n.onDirty(),p.appendChild(j("Consecutive anomalies",g));let S=document.createElement("select");S.className="dtk-ui-select";for(let E of["same","any","up","down"]){let T=document.createElement("option");T.value=E,T.textContent=E,S.appendChild(T)}S.value=typeof a.direction=="string"?a.direction:"same",S.onchange=()=>n.onDirty(),p.appendChild(j("Direction",S));let N=document.createElement("label");N.className="dtk-ui-check";let L=document.createElement("input");L.type="checkbox",L.checked=a.no_data_alert===!0,L.onchange=()=>n.onDirty(),N.append(L,document.createTextNode("Alert on missing data")),p.appendChild(N);let _=document.createElement("label");_.className="dtk-ui-check";let P=document.createElement("input");P.type="checkbox",P.checked=a.notify_on_recovery===!0,P.onchange=()=>n.onDirty(),_.append(P,document.createTextNode("Notify on recovery")),p.appendChild(_);let I=document.createElement("input");I.type="text",I.className="dtk-ui-input",I.placeholder="e.g. 30min (optional)",I.value=a.alert_cooldown!==void 0&&a.alert_cooldown!==null?String(a.alert_cooldown):"",I.oninput=()=>n.onDirty(),p.appendChild(j("Alert cooldown",I));let z=a.min_detectors!==void 0||a.anomaly_window!==void 0||a.min_anomaly_share!==void 0||Array.isArray(a.mentions)&&a.mentions.length>0||!!a.dashboard_url,K=Ut("Advanced");K.details.open=z,p.appendChild(K.details);let q=document.createElement("input");q.type="number",q.min="1",q.step="1",q.className="dtk-ui-input",q.value=String(typeof a.min_detectors=="number"?a.min_detectors:1),q.oninput=()=>n.onDirty(),K.body.appendChild(j("Min detectors",q));let X=$("div","dtk-ui-row2"),U=document.createElement("input");U.type="text",U.className="dtk-ui-input",U.placeholder="e.g. 30min",U.value=a.anomaly_window!==void 0&&a.anomaly_window!==null?String(a.anomaly_window):"",U.oninput=()=>n.onDirty();let B=document.createElement("input");B.type="number",B.min="0",B.max="1",B.step="0.05",B.className="dtk-ui-input",B.placeholder="e.g. 0.3",B.value=typeof a.min_anomaly_share=="number"?String(a.min_anomaly_share):"",B.oninput=()=>n.onDirty(),X.append(j("Anomaly window",U),j("Min anomaly share",B)),K.body.appendChild(X);let le=Array.isArray(a.mentions)?a.mentions.map(String):[],ne=Me(le,"add mention, Enter",()=>n.onDirty());K.body.appendChild(j("Mentions",ne.el));let J=document.createElement("input");if(J.type="text",J.className="dtk-ui-input",J.placeholder="https://\u2026",J.value=typeof a.dashboard_url=="string"?a.dashboard_url:"",J.oninput=()=>n.onDirty(),K.body.appendChild(j("Dashboard URL",J)),Object.keys(s).length>0){let E=$("span","dtk-ui-preschip");E.textContent=`+${Object.keys(s).length} fields preserved`,p.appendChild(E)}function V(){p.style.display=l.checked?"":"none"}return l.onchange=()=>{V(),n.onDirty()},V(),{el:r,getData:()=>{if(!l.checked)return;let E={enabled:m.checked},T=[...t.channels.filter(F=>w.has(F.name)).map(F=>F.name),...k.get()];T.length>0&&(E.channels=T);let Y=Number(g.value);E.consecutive_anomalies=g.value.trim()!==""&&Number.isFinite(Y)?Math.max(1,Math.round(Y)):3,E.direction=S.value,E.no_data_alert=L.checked,E.notify_on_recovery=P.checked;let W=I.value.trim();W&&(E.alert_cooldown=je(W));let Q=Number(q.value),oe=q.value.trim()!==""&&Number.isFinite(Q)?Math.max(1,Math.round(Q)):1;oe!==1&&(E.min_detectors=oe);let me=U.value.trim(),D=B.value.trim();me&&D&&(E.anomaly_window=je(me),E.min_anomaly_share=Number(D));let Z=ne.get();Z.length>0&&(E.mentions=Z);let ie=J.value.trim();ie&&(E.dashboard_url=ie);for(let[F,ee]of Object.entries(s))Bt.has(F)||(E[F]=ee);return E},issues:()=>{if(!l.checked)return[];let E=U.value.trim(),T=B.value.trim();return E===""!=(T==="")?["anomaly window and min anomaly share must be set together"]:[]}}}function Jt(e){if(typeof e=="string")return{instructions:e,synonyms:[],examples:[]};if(e&&typeof e=="object"){let t=e;return{instructions:typeof t.instructions=="string"?t.instructions:"",synonyms:Array.isArray(t.synonyms)?t.synonyms.map(String):[],examples:Array.isArray(t.examples)?t.examples.map(String):[]}}return{instructions:"",synonyms:[],examples:[]}}function In(e,t){let n=Jt(e==null?void 0:e.ai_context),{root:r,body:i}=xe("AI context"),o=document.createElement("details");o.className="dtk-ui-form-adv",o.open=!!(n.instructions||n.synonyms.length>0||n.examples.length>0);let a=document.createElement("summary");a.className="dtk-ui-form-adv-summary",a.textContent="Instructions / synonyms / examples",o.appendChild(a);let s=$("div","dtk-ui-form-adv-body");o.appendChild(s),i.appendChild(o);let f=document.createElement("textarea");f.className="dtk-ui-input dtk-ui-textarea",f.rows=3,f.placeholder="Business meaning / how to interpret this metric",f.value=n.instructions,f.oninput=()=>t.onDirty(),s.appendChild(j("Instructions",f));let l=Me(n.synonyms,"add synonym, Enter",()=>t.onDirty());s.appendChild(j("Synonyms",l.el));let p=Me(n.examples,"add example, Enter",()=>t.onDirty());return s.appendChild(j("Examples",p.el)),{el:r,getData:()=>{let h=f.value.trim(),m=l.get(),c=p.get();if(!h&&m.length===0&&c.length===0)return;let v={};return h&&(v.instructions=h),m.length>0&&(v.synonyms=m),c.length>0&&(v.examples=c),v},setData:h=>{let m=Jt(h!=null?h:null);f.value=m.instructions,l.set(m.synonyms),p.set(m.examples),(m.instructions||m.synonyms.length>0||m.examples.length>0)&&(o.open=!0)}}}var An=`SELECT
472
472
  toStartOfInterval(event_time, INTERVAL {{ interval_seconds }} SECOND) AS timestamp,
473
473
  count() AS value
474
474
  FROM my_table
@@ -476,7 +476,7 @@ WHERE event_time >= '{{ dtk_start_time }}'
476
476
  AND event_time < '{{ dtk_end_time }}'
477
477
  GROUP BY timestamp
478
478
  ORDER BY timestamp
479
- `;function Pn(e){let t=e.trim();return/^\d+$/.test(t)?`${t}s`:t}function Hn(e,t,n,r,i){let o=$("div","dtk-ui-form-query"),a=$("div","dtk-ui-subtabs"),s=document.createElement("button");s.type="button",s.className="dtk-ui-subtab on",s.textContent="SQL";let f=document.createElement("button");f.type="button",f.className="dtk-ui-subtab",f.textContent="From OSI",a.append(s,f),o.appendChild(a);let l=$("div","dtk-ui-subtab-panes");o.appendChild(l);let p=$("div","dtk-ui-subtab-pane"),h=$("div","dtk-ui-subtab-pane");h.style.display="none",l.append(p,h);function m(D){s.classList.toggle("on",D==="sql"),f.classList.toggle("on",D==="osi"),p.style.display=D==="sql"?"":"none",h.style.display=D==="osi"?"":"none"}s.onclick=()=>m("sql"),f.onclick=()=>m("osi");let c=typeof(e==null?void 0:e.query_file)=="string"?e.query_file:null,v=c?"file":"inline",w=c!=null?c:"",M=typeof(e==null?void 0:e.query)=="string"?e.query:e===null&&!c?An:"",u=null;if(c){u=document.createElement("div");let D=document.createElement("input");D.type="text",D.className="dtk-ui-input",D.readOnly=!0,D.value=c,u.appendChild(j("Query file (read-only)",D)),p.appendChild(u)}let x=Ft({value:M,onInput:()=>t.onDirty(),ariaLabel:"Metric SQL query"});p.appendChild(x.el),c&&x.setDisabled(!0,`SQL lives in ${c} \u2014 edit it on disk, or switch this metric to an inline query in the YAML tab.`);let b=document.createElement("textarea");b.className="dtk-ui-input dtk-ui-textarea",b.rows=8,b.placeholder="Paste an OSI semantic model (YAML)",h.appendChild(j("OSI model",b));let k=$("div","dtk-ui-form-hint-status");h.appendChild(k);let g=document.createElement("select");g.className="dtk-ui-select",g.disabled=!0,h.appendChild(j("Metric",g));let S=document.createElement("select");S.className="dtk-ui-select";for(let D of["clickhouse","cube"]){let X=document.createElement("option");X.value=D,X.textContent=D,S.appendChild(X)}h.appendChild(j("Target",S));let N=document.createElement("select");N.className="dtk-ui-select";let L=j("Dataset (optional)",N);h.appendChild(L);let _=document.createElement("input");_.type="text",_.className="dtk-ui-input",_.placeholder="time column (auto-detected if omitted)";let A=j("Time field (optional)",_);h.appendChild(A);let I=document.createElement("input");I.type="text",I.className="dtk-ui-input",I.placeholder="extra WHERE condition (optional)",h.appendChild(j("Where (optional)",I));let z=document.createElement("input");z.type="text",z.className="dtk-ui-input",z.placeholder="cube name";let K=j("Cube (optional)",z);h.appendChild(K);let q=document.createElement("input");q.type="text",q.className="dtk-ui-input",q.placeholder="measure name (defaults to the metric name)";let G=j("Cube measure (optional)",q);h.appendChild(G);let U=document.createElement("input");U.type="text",U.className="dtk-ui-input",U.placeholder="time dimension";let B=j("Time dimension (cube)",U);h.appendChild(B);function le(){let D=S.value==="cube";L.style.display=D?"none":"",A.style.display=D?"none":"",K.style.display=D?"":"none",G.style.display=D?"":"none",B.style.display=D?"":"none"}S.onchange=()=>{le()},le();let ne=document.createElement("button");ne.type="button",ne.className="dtk-ui-btn primary",ne.textContent="Compile",h.appendChild(ne);let J=$("div","dtk-ui-form-err");h.appendChild(J);let V=$("div","dtk-ui-form-warn");h.appendChild(V);let E=[],T=null;function Y(){let D=g.value.indexOf("::");return D===-1?-1:Number(g.value.slice(0,D))}function W(){let D=g.value.indexOf("::");return D===-1?g.value:g.value.slice(D+2)}function Q(){var ie;N.innerHTML="";let D=document.createElement("option");D.value="",D.textContent="(auto)",N.appendChild(D);let X=E[Y()];for(let F of(ie=X==null?void 0:X.datasets)!=null?ie:[]){let ee=document.createElement("option");ee.value=F,ee.textContent=F,N.appendChild(ee)}}function oe(){if(g.innerHTML="",E.length===0){g.disabled=!0,Q();return}g.disabled=!1;let D=E.length>1;E.forEach((X,ie)=>{let F=D?document.createElement("optgroup"):null;F&&(F.label=X.name,g.appendChild(F));for(let ee of X.metrics){let ce=document.createElement("option");ce.value=`${ie}::${ee}`,ce.textContent=ee,(F!=null?F:g).appendChild(ce)}}),Q()}g.onchange=()=>{Q()};let me=it(()=>{let D=b.value;if(D.trim()===""){E=[],oe(),k.textContent="",J.textContent="";return}k.textContent="Inspecting\u2026",t.osiInspect(D).then(X=>{E=X.models,oe();let ie=E.reduce((F,ee)=>F+ee.metrics.length,0);k.textContent=ie>0?`Found ${ie} metric(s) across ${E.length} model(s).`:"No metrics found.",J.textContent=""}).catch(X=>{E=[],oe(),k.textContent="",J.textContent=X.message})},400);return b.addEventListener("input",()=>{me()}),ne.onclick=()=>{J.textContent="",V.innerHTML="";let D=b.value;if(D.trim()===""){J.textContent="paste an OSI model first";return}if(g.value===""){J.textContent="pick a metric to import";return}let X=n();if(!X){J.textContent="set an interval in Schedule & loading first";return}let ie=S.value,F={text:D,metric:W(),interval:Pn(X),target:ie},ee=r();ee.length>0&&(F.seasonality=ee),ie==="clickhouse"?(N.value&&(F.dataset=N.value),_.value.trim()&&(F.time_field=_.value.trim()),I.value.trim()&&(F.where=I.value.trim())):(z.value.trim()&&(F.cube=z.value.trim()),q.value.trim()&&(F.cube_measure=q.value.trim()),U.value.trim()&&(F.time_dimension=U.value.trim()),I.value.trim()&&(F.where=I.value.trim()));let ce=W();ne.disabled=!0,ne.textContent="Compiling\u2026",t.osiImport(F).then(te=>{let fe=typeof te.body.query=="string"?te.body.query:te.sql;if(x.setValue(fe),x.setDisabled(!1),u&&(u.style.display="none"),v="inline",w="",m("sql"),T={fingerprint:te.fingerprint,target:te.target,metric:ce},i(te.body,te.name||ce),te.warnings.length>0)for(let we of te.warnings){let he=document.createElement("div");he.textContent=`\u26A0 ${we}`,V.appendChild(he)}t.onDirty()}).catch(te=>{J.textContent=te.message}).finally(()=>{ne.disabled=!1,ne.textContent="Compile"})},{el:o,getMode:()=>v,getQuery:()=>x.getValue(),getFilePath:()=>w,getOsiProvenance:()=>T,issues:()=>v==="inline"&&x.getValue().trim()===""?["query is empty"]:[]}}function Kt(e,t,n,r){let i=document.createElement("div");i.className="dtk-ui-form";let o,a,s,f,l,p,h,m={};function c(u){i.innerHTML="";let x=$("div","dtk-ui-form-rail"),b=$("div","dtk-ui-form-main");i.append(x,b),m=wn(u),o=Cn(u,t,n),a=Tn(u,n),s=Nn(u,n),f=Dn(u,n,r.mode),l=On(u,t,n),p=In(u,n),x.append(o.el,a.el,s.el,f.el,l.el,p.el),Object.keys(m).length>0&&x.appendChild(En(m)),h=Hn(u,n,()=>a.getInterval(),()=>s.getColumns(),(k,g)=>{r.mode==="create"&&o.getName()===""&&o.setName(g),typeof k.description=="string"&&o.setDescription(k.description),k.ai_context&&typeof k.ai_context=="object"&&p.setData(k.ai_context),Array.isArray(k.seasonality_columns)&&s.setColumns(k.seasonality_columns.map(String))}),b.appendChild(h.el)}c(e);function v(){let u={},x=o.getName();x&&(u.name=x);let b=o.getDescription().trim();b&&(u.description=b);let k=o.getTags();k.length>0&&(u.tags=k);let g=o.getProfile();g&&(u.profile=g);let S=p.getData();S&&(u.ai_context=S),h.getMode()==="file"?u.query_file=h.getFilePath():u.query=h.getQuery();let N=a.getQueryColumns();N&&(u.query_columns=N);let L=a.getInterval();L&&(u.interval=je(L));let _=a.getLoadingStartTime();_&&(u.loading_start_time=_);let A=a.getLoadingDelay();A&&(u.loading_delay=je(A));let I=a.getLoadingBatchSize();I!==null&&(u.loading_batch_size=I);let z=s.getColumns();z.length>0&&(u.seasonality_columns=z);let K=f.getData();K.length>0&&(u.detectors=K);let q=l.getData();q!==void 0&&(u.alerting=q);for(let[G,U]of Object.entries(m))u[G]=U;return o.getEnabled()||(u.enabled=!1),u}function w(){let u=v(),x=h.getOsiProvenance(),b=x?[`Query compiled from OSI metric '${x.metric}' (target: ${x.target}, sql-fingerprint: ${x.fingerprint}).`,"Review the SQL before saving \u2014 re-compiling after the OSI model changes is a visible diff."]:void 0;return jt(u,b)}function M(){return[...o.issues(),...a.issues(),...h.issues(),...f.issues(),...l.issues()]}return{el:i,setFromData:u=>c(u),toData:v,toYaml:w,clientIssues:M,focusFirst:()=>o.focusName()}}var Wt=`# New metric \u2014 edit the query and name, then Create.
479
+ `;function Pn(e){let t=e.trim();return/^\d+$/.test(t)?`${t}s`:t}function Hn(e,t,n,r,i){let o=$("div","dtk-ui-form-query"),a=$("div","dtk-ui-subtabs"),s=document.createElement("button");s.type="button",s.className="dtk-ui-subtab on",s.textContent="SQL";let f=document.createElement("button");f.type="button",f.className="dtk-ui-subtab",f.textContent="From OSI",a.append(s,f),o.appendChild(a);let l=$("div","dtk-ui-subtab-panes");o.appendChild(l);let p=$("div","dtk-ui-subtab-pane"),h=$("div","dtk-ui-subtab-pane");h.style.display="none",l.append(p,h);function m(D){s.classList.toggle("on",D==="sql"),f.classList.toggle("on",D==="osi"),p.style.display=D==="sql"?"":"none",h.style.display=D==="osi"?"":"none"}s.onclick=()=>m("sql"),f.onclick=()=>m("osi");let c=typeof(e==null?void 0:e.query_file)=="string"?e.query_file:null,v=c?"file":"inline",w=c!=null?c:"",M=typeof(e==null?void 0:e.query)=="string"?e.query:e===null&&!c?An:"",u=null;if(c){u=document.createElement("div");let D=document.createElement("input");D.type="text",D.className="dtk-ui-input",D.readOnly=!0,D.value=c,u.appendChild(j("Query file (read-only)",D)),p.appendChild(u)}let x=Ft({value:M,onInput:()=>t.onDirty(),ariaLabel:"Metric SQL query"});p.appendChild(x.el),c&&x.setDisabled(!0,`SQL lives in ${c} \u2014 edit it on disk, or switch this metric to an inline query in the YAML tab.`);let b=document.createElement("textarea");b.className="dtk-ui-input dtk-ui-textarea",b.rows=8,b.placeholder="Paste an OSI semantic model (YAML)",h.appendChild(j("OSI model",b));let k=$("div","dtk-ui-form-hint-status");h.appendChild(k);let g=document.createElement("select");g.className="dtk-ui-select",g.disabled=!0,h.appendChild(j("Metric",g));let S=document.createElement("select");S.className="dtk-ui-select";for(let D of["clickhouse","cube"]){let Z=document.createElement("option");Z.value=D,Z.textContent=D,S.appendChild(Z)}h.appendChild(j("Target",S));let N=document.createElement("select");N.className="dtk-ui-select";let L=j("Dataset (optional)",N);h.appendChild(L);let _=document.createElement("input");_.type="text",_.className="dtk-ui-input",_.placeholder="time column (auto-detected if omitted)";let P=j("Time field (optional)",_);h.appendChild(P);let I=document.createElement("input");I.type="text",I.className="dtk-ui-input",I.placeholder="extra WHERE condition (optional)",h.appendChild(j("Where (optional)",I));let z=document.createElement("input");z.type="text",z.className="dtk-ui-input",z.placeholder="cube name";let K=j("Cube (optional)",z);h.appendChild(K);let q=document.createElement("input");q.type="text",q.className="dtk-ui-input",q.placeholder="measure name (defaults to the metric name)";let X=j("Cube measure (optional)",q);h.appendChild(X);let U=document.createElement("input");U.type="text",U.className="dtk-ui-input",U.placeholder="time dimension";let B=j("Time dimension (cube)",U);h.appendChild(B);function le(){let D=S.value==="cube";L.style.display=D?"none":"",P.style.display=D?"none":"",K.style.display=D?"":"none",X.style.display=D?"":"none",B.style.display=D?"":"none"}S.onchange=()=>{le()},le();let ne=document.createElement("button");ne.type="button",ne.className="dtk-ui-btn primary",ne.textContent="Compile",h.appendChild(ne);let J=$("div","dtk-ui-form-err");h.appendChild(J);let V=$("div","dtk-ui-form-warn");h.appendChild(V);let E=[],T=null;function Y(){let D=g.value.indexOf("::");return D===-1?-1:Number(g.value.slice(0,D))}function W(){let D=g.value.indexOf("::");return D===-1?g.value:g.value.slice(D+2)}function Q(){var ie;N.innerHTML="";let D=document.createElement("option");D.value="",D.textContent="(auto)",N.appendChild(D);let Z=E[Y()];for(let F of(ie=Z==null?void 0:Z.datasets)!=null?ie:[]){let ee=document.createElement("option");ee.value=F,ee.textContent=F,N.appendChild(ee)}}function oe(){if(g.innerHTML="",E.length===0){g.disabled=!0,Q();return}g.disabled=!1;let D=E.length>1;E.forEach((Z,ie)=>{let F=D?document.createElement("optgroup"):null;F&&(F.label=Z.name,g.appendChild(F));for(let ee of Z.metrics){let ce=document.createElement("option");ce.value=`${ie}::${ee}`,ce.textContent=ee,(F!=null?F:g).appendChild(ce)}}),Q()}g.onchange=()=>{Q()};let me=it(()=>{let D=b.value;if(D.trim()===""){E=[],oe(),k.textContent="",J.textContent="";return}k.textContent="Inspecting\u2026",t.osiInspect(D).then(Z=>{E=Z.models,oe();let ie=E.reduce((F,ee)=>F+ee.metrics.length,0);k.textContent=ie>0?`Found ${ie} metric(s) across ${E.length} model(s).`:"No metrics found.",J.textContent=""}).catch(Z=>{E=[],oe(),k.textContent="",J.textContent=Z.message})},400);return b.addEventListener("input",()=>{me()}),ne.onclick=()=>{J.textContent="",V.innerHTML="";let D=b.value;if(D.trim()===""){J.textContent="paste an OSI model first";return}if(g.value===""){J.textContent="pick a metric to import";return}let Z=n();if(!Z){J.textContent="set an interval in Schedule & loading first";return}let ie=S.value,F={text:D,metric:W(),interval:Pn(Z),target:ie},ee=r();ee.length>0&&(F.seasonality=ee),ie==="clickhouse"?(N.value&&(F.dataset=N.value),_.value.trim()&&(F.time_field=_.value.trim()),I.value.trim()&&(F.where=I.value.trim())):(z.value.trim()&&(F.cube=z.value.trim()),q.value.trim()&&(F.cube_measure=q.value.trim()),U.value.trim()&&(F.time_dimension=U.value.trim()),I.value.trim()&&(F.where=I.value.trim()));let ce=W();ne.disabled=!0,ne.textContent="Compiling\u2026",t.osiImport(F).then(te=>{let fe=typeof te.body.query=="string"?te.body.query:te.sql;if(x.setValue(fe),x.setDisabled(!1),u&&(u.style.display="none"),v="inline",w="",m("sql"),T={fingerprint:te.fingerprint,target:te.target,metric:ce},i(te.body,te.name||ce),te.warnings.length>0)for(let we of te.warnings){let he=document.createElement("div");he.textContent=`\u26A0 ${we}`,V.appendChild(he)}t.onDirty()}).catch(te=>{J.textContent=te.message}).finally(()=>{ne.disabled=!1,ne.textContent="Compile"})},{el:o,getMode:()=>v,getQuery:()=>x.getValue(),getFilePath:()=>w,getOsiProvenance:()=>T,issues:()=>v==="inline"&&x.getValue().trim()===""?["query is empty"]:[]}}function Kt(e,t,n,r){let i=document.createElement("div");i.className="dtk-ui-form";let o,a,s,f,l,p,h,m={};function c(u){i.innerHTML="";let x=$("div","dtk-ui-form-rail"),b=$("div","dtk-ui-form-main");i.append(x,b),m=wn(u),o=Cn(u,t,n),a=Tn(u,n),s=Nn(u,n),f=Dn(u,n,r.mode),l=On(u,t,n),p=In(u,n),x.append(o.el,a.el,s.el,f.el,l.el,p.el),Object.keys(m).length>0&&x.appendChild(En(m)),h=Hn(u,n,()=>a.getInterval(),()=>s.getColumns(),(k,g)=>{r.mode==="create"&&(o.getName()===""||o.getName()==="my_metric")&&o.setName(g),typeof k.description=="string"&&o.setDescription(k.description),k.ai_context&&typeof k.ai_context=="object"&&p.setData(k.ai_context),Array.isArray(k.seasonality_columns)&&s.setColumns(k.seasonality_columns.map(String))}),b.appendChild(h.el)}c(e);function v(){let u={},x=o.getName();x&&(u.name=x);let b=o.getDescription().trim();b&&(u.description=b);let k=o.getTags();k.length>0&&(u.tags=k);let g=o.getProfile();g&&(u.profile=g);let S=p.getData();S&&(u.ai_context=S),h.getMode()==="file"?u.query_file=h.getFilePath():u.query=h.getQuery();let N=a.getQueryColumns();N&&(u.query_columns=N);let L=a.getInterval();L&&(u.interval=je(L));let _=a.getLoadingStartTime();_&&(u.loading_start_time=_);let P=a.getLoadingDelay();P&&(u.loading_delay=je(P));let I=a.getLoadingBatchSize();I!==null&&(u.loading_batch_size=I);let z=s.getColumns();z.length>0&&(u.seasonality_columns=z);let K=f.getData();K.length>0&&(u.detectors=K);let q=l.getData();q!==void 0&&(u.alerting=q);for(let[X,U]of Object.entries(m))u[X]=U;return o.getEnabled()||(u.enabled=!1),u}function w(){let u=v(),x=h.getOsiProvenance(),b=x?[`Query compiled from OSI metric '${x.metric}' (target: ${x.target}, sql-fingerprint: ${x.fingerprint}).`,"Review the SQL before saving \u2014 re-compiling after the OSI model changes is a visible diff."]:void 0;return jt(u,b)}function M(){return[...o.issues(),...a.issues(),...h.issues(),...f.issues(),...l.issues()]}return{el:i,setFromData:u=>c(u),toData:v,toYaml:w,clientIssues:M,focusFirst:()=>o.focusName()}}var Wt=`# New metric \u2014 edit the query and name, then Create.
480
480
  # Template variables available in query:
481
481
  # {{ dtk_start_time }} / {{ dtk_end_time }} \u2014 load window bounds
482
482
  # {{ interval_seconds }} \u2014 metric interval in seconds
@@ -507,9 +507,12 @@ detectors:
507
507
  # channels: [my_channel]
508
508
  # consecutive_anomalies: 3
509
509
  `,jn={channels:[],profiles:[],default_profile:null};function et(e,t,n){var we,he;let r=t.mode==="create",i=(we=t.meta)!=null?we:jn,o=r||t.data!==null&&t.data!==void 0,a=!1,s=!1,f=!1,l=!1,p=o?"builder":"yaml",h=He(e,{modalClass:"dtk-ui-editor-modal",onRequestClose:()=>{fe()}}),{modal:m}=h,c=document.createElement("div");c.className="dtk-ui-editor-head";let v=document.createElement("div");v.className="dtk-ui-editor-titlewrap";let w=document.createElement("span");if(w.className="dtk-ui-editor-title",w.textContent=r?"New metric":`Edit ${(he=t.name)!=null?he:""}`,v.appendChild(w),!r&&t.file){let C=document.createElement("span");C.className="dtk-ui-editor-sub",C.textContent=t.file,v.appendChild(C)}c.appendChild(v);let M=document.createElement("div");M.className="dtk-ui-edtabs";let u=document.createElement("button");u.type="button",u.className="dtk-ui-edtab",u.textContent="Builder",o||(u.disabled=!0,u.title=t.parseError?`This file doesn't parse as a metric config \u2014 fix it in YAML first.
510
- ${t.parseError}`:"Builder unavailable for this file"),u.onclick=()=>{oe("builder")};let x=document.createElement("button");x.type="button",x.className="dtk-ui-edtab",x.textContent="YAML",x.onclick=()=>{oe("yaml")},M.append(u,x),c.appendChild(M);let b=document.createElement("button");b.type="button",b.className="dtk-ui-overlay-close",b.textContent="\u2715",b.title="Close (Esc)",b.onclick=()=>{fe()},c.appendChild(b),m.appendChild(c);let k=document.createElement("div");k.className="dtk-ui-editor-body",m.appendChild(k);let g=null;if(r){let C=document.createElement("div");C.className="dtk-ui-editor-field",C.innerHTML='<span class="dtk-ui-field-label">Folder</span>',g=document.createElement("input"),g.type="text",g.className="dtk-ui-input",g.placeholder="optional subfolder under metrics/",C.appendChild(g);let P=document.createElement("div");P.className="dtk-ui-editor-hint",P.textContent="The file is written as metrics/[folder/]<name>.yml \u2014 <name> comes from the metric name below.",C.appendChild(P),k.appendChild(C)}let S=!r&&/(^|\n)\s*#/.test(t.text),N=document.createElement("div");N.className="dtk-ui-editor-note",N.textContent="Saving from Builder re-writes the YAML \u2014 comments are dropped (the previous file is archived to metrics/.history/).",N.style.display="none",k.appendChild(N);let L=document.createElement("div");L.className="dtk-ui-editor-panes",k.appendChild(L);let _=document.createElement("textarea");_.className="dtk-ui-editor-textarea",_.spellcheck=!1,_.autocapitalize="off",_.value=t.text,_.addEventListener("input",()=>{a=!0,l=!0,T()}),_.addEventListener("keydown",C=>{if(C.key==="Tab"){C.preventDefault();let P=_.selectionStart,Z=_.selectionEnd;_.value=`${_.value.slice(0,P)} ${_.value.slice(Z)}`,_.selectionStart=_.selectionEnd=P+2,a=!0,l=!0,T()}});let A=null;o&&(A=Kt(r?null:t.data,i,{onDirty:()=>{a=!0,f=!0,T()},osiInspect:C=>gt(C),osiImport:C=>ht(C)},{mode:t.mode}),L.appendChild(A.el)),L.appendChild(_),m.addEventListener("keydown",C=>{(C.ctrlKey||C.metaKey)&&C.key.toLowerCase()==="s"&&(C.preventDefault(),ee())});let I=document.createElement("div");I.className="dtk-ui-editor-error",I.style.display="none",m.appendChild(I);function z(C){I.textContent=C,I.style.display=""}function K(){I.style.display="none",I.textContent=""}let q=document.createElement("div");q.className="dtk-ui-editor-foot";let G=document.createElement("div");G.className="dtk-ui-editor-foot-left";let U=document.createElement("div");U.className="dtk-ui-editor-foot-right",q.append(G,U),m.appendChild(q);let B=document.createElement("span");B.className="dtk-ui-validchip",B.style.display="none",U.appendChild(B);let le=document.createElement("button");le.type="button",le.className="dtk-ui-btn primary";let ne=r?"Create metric":"Save changes";le.textContent=ne,le.onclick=()=>{ee()},U.appendChild(le);let J,V=0;function E(C,P,Z){B.className=`dtk-ui-validchip ${C}`,B.innerHTML='<span class="dtk-ui-validchip-dot"></span>',B.appendChild(document.createTextNode(P)),B.title=Z!=null?Z:P,B.style.display=""}function T(){J!==void 0&&window.clearTimeout(J),J=window.setTimeout(()=>{J=void 0,Y()},600)}async function Y(){let C=++V,P;if(p==="builder"&&A){let Z=A.clientIssues();if(Z.length>0){E("warn",Z[0],Z.join(`
511
- `));return}P=A.toYaml()}else P=_.value;E("checking","checking\u2026");try{if(await Ye(P),C!==V)return;E("ok","\u2713 valid config")}catch(Z){if(C!==V)return;let d=Z.message;E("err",d.split(`
512
- `)[0],d)}}function W(){u.classList.toggle("on",p==="builder"),x.classList.toggle("on",p==="yaml"),A&&(A.el.style.display=p==="builder"?"":"none"),_.style.display=p==="yaml"?"":"none",N.style.display=p==="builder"&&S?"":"none"}let Q=!1;async function oe(C){if(!(C===p||s||Q)&&!(C==="builder"&&!A)){Q=!0;try{await me(C)}finally{Q=!1}}}async function me(C){if(C==="yaml")f&&A&&(_.value=A.toYaml(),f=!1,l=!1);else if(l&&A){K();try{let P=await Ye(_.value);A.setFromData(P.data),l=!1,f=!1}catch(P){z(`Fix the YAML before switching to Builder:
513
- ${P.message}`);return}}K(),p=C,W(),T()}function D(){let C=document.createElement("button");return C.type="button",C.className="dtk-ui-btn danger",C.textContent="Delete metric\u2026",C.onclick=()=>X(),C}r||G.appendChild(D());function X(){var ae,re;let C=(ae=t.name)!=null?ae:"",P=(re=t.file)!=null?re:"";G.innerHTML="";let Z=document.createElement("div");Z.className="dtk-ui-editor-confirm";let d=document.createElement("span");d.className="dtk-ui-editor-confirm-text",d.innerHTML=`Delete <b>${O(C)}</b>? The file <code>${O(P)}</code> is archived to <code>metrics/.history/</code> and removed. Data rows in the <code>_dtk_*</code> tables remain until <code>dtk clean</code>.`,Z.appendChild(d);let y=document.createElement("button");y.type="button",y.className="dtk-ui-btn danger",y.textContent="Delete metric",y.onclick=()=>{ce(C,y,R)};let R=document.createElement("button");R.type="button",R.className="dtk-ui-btn",R.textContent="Cancel",R.onclick=()=>{G.innerHTML="",G.appendChild(D())},Z.append(y,R),G.appendChild(Z)}function ie(C){s=C,le.disabled=C,le.textContent=C?"Saving\u2026":ne}function F(){return p==="builder"&&A&&f?A.toYaml():_.value}async function ee(){var C;if(!s){if(K(),p==="builder"&&A){let P=A.clientIssues();if(P.length>0){z(`Fix before saving:
514
- - ${P.join(`
515
- - `)}`);return}}ie(!0);try{let P=F(),Z=r?await mt({text:P,folder:(g==null?void 0:g.value.trim())||void 0}):await ft((C=t.name)!=null?C:"",{text:P,digest:t.digest});a=!1,n.onSaved(Z,t.mode),te()}catch(P){z(P.message)}finally{ie(!1)}}}async function ce(C,P,Z){if(!s){K(),s=!0,P.disabled=!0,Z.disabled=!0,P.textContent="Deleting\u2026";try{let d=await bt(C);a=!1,n.onDeleted(d),te()}catch(d){z(d.message),P.disabled=!1,Z.disabled=!1,P.textContent="Delete metric"}finally{s=!1}}}function te(){h.close(),n.onClose()}function fe(){return a&&!window.confirm("Discard unsaved changes?")?!1:(te(),!0)}return W(),p==="builder"&&A?A.focusFirst():_.focus(),T(),{close:te,requestClose:fe}}function Qt(e,t,n){if(e.innerHTML="",!t)return;let r=document.createElement("div");r.className="dtk-ui-nextsteps"+(t.phase==="failed"?" failed":"");let i=document.createElement("span");i.className="dtk-ui-nextsteps-text";let o=document.createElement("b");if(o.textContent=t.metric,t.phase==="done")i.append("Data loaded for ",o," \u2014 tune the detector on the real series.");else if(t.phase==="running"){i.append("Loading data for ",o,"\u2026 ");let p=document.createElement("a");p.href="#",p.className="dtk-ui-nextsteps-link",p.textContent="view log",p.onclick=h=>{h.preventDefault(),t.jobId&&n.onFollow(t.jobId)},i.appendChild(p)}else t.phase==="failed"?i.append("Loading data for ",o," failed \u2014 check the log, then retry."):i.append("Metric ",o," created. Next: load data, then tune the detector on it.");r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-nextsteps-actions";let s=document.createElement("button");if(s.type="button",s.className="dtk-ui-btn"+(t.phase==="idle"||t.phase==="failed"?" primary":""),s.textContent=t.phase==="failed"?"Retry load":"Load & detect",s.disabled=t.phase==="running",s.title="Runs `dtk run --steps load,detect` for this metric (no alerts sent)",s.onclick=()=>n.onLoad(t.metric),a.appendChild(s),t.phase==="running"){let p=document.createElement("span");p.className="dtk-ui-nextsteps-spin",a.appendChild(p)}let f=document.createElement("button");f.type="button",f.className="dtk-ui-btn"+(t.phase==="done"?" primary":""),f.textContent="Open tune",f.disabled=t.phase!=="done",f.title=t.phase==="done"?"Open the tune cockpit on the loaded series":"Load data first \u2014 the tune cockpit works on persisted datapoints",f.onclick=()=>n.onTune(t.metric),a.appendChild(f);let l=document.createElement("button");l.type="button",l.className="dtk-ui-nextsteps-close",l.textContent="\u2715",l.title="Dismiss",l.onclick=()=>n.onDismiss(),a.appendChild(l),r.appendChild(a),e.appendChild(r)}var qn=3,Fn=[{value:"24h",label:"24h"},{value:"7d",label:"7d"},{value:"30d",label:"30d"},{value:"90d",label:"90d"},{value:"all",label:"All"}];function zn(e,t){var Z;yt(),t.classList.add(ve),t.innerHTML="";let n=document.createElement("div");n.className=`${ve}-root`,t.appendChild(n);let r=e.metrics,i=(Z=e.form_meta)!=null?Z:{channels:[],profiles:[],default_profile:null},o={windowPreset:e.initial_window||"30d",metrics:r.map(Se),jobs:[],followedJobId:null,followOffset:0,tagFilter:null,sort:{key:"alerts",dir:Ke.alerts},nextSteps:null},a=null,s=null,f,l;function p(){let d=new Set,y=new Set;for(let R of r){d.add(R.name);for(let ae of R.tags)y.add(ae)}return[...d,...[...y].map(R=>`tag:${R}`)]}function h(){let d=o.jobs.find(y=>y.status==="running"&&y.kind!=="tune");return d?{busy:!0,reason:`a pipeline job is already running (${d.label})`}:{busy:!1,reason:""}}let m=document.createElement("div");m.className="dtk-ui-header",n.appendChild(m);let c=document.createElement("div");c.className="dtk-ui-brand",c.innerHTML=`<span class="dtk-ui-brand-dot"></span><span class="dtk-ui-brand-name">detectkit \xB7 <b>${O(e.project)}</b></span>`,m.appendChild(c);let v=document.createElement("div");v.className="dtk-ui-header-right",m.appendChild(v);let w=document.createElement("div");w.className="dtk-ui-seg";for(let d of Fn){let y=document.createElement("button");y.type="button",y.className="dtk-ui-seg-btn"+(o.windowPreset===d.value?" on":""),y.textContent=d.label,y.onclick=()=>{o.windowPreset!==d.value&&(o.windowPreset=d.value,w.querySelectorAll(".dtk-ui-seg-btn").forEach(R=>R.classList.remove("on")),y.classList.add("on"),a&&a.setWindow(d.value),P())},w.appendChild(y)}v.appendChild(w);let M=document.createElement("button");M.type="button",M.className="dtk-ui-iconbtn",M.title="Refresh overview",M.textContent="\u27F3",M.onclick=()=>{P()},v.appendChild(M);let u=document.createElement("button");u.type="button",u.className="dtk-ui-runbtn",u.textContent="Run pipeline",u.onclick=()=>T(),v.appendChild(u);let x=document.createElement("button");x.type="button",x.className="dtk-ui-newbtn",x.textContent="New metric",x.onclick=()=>G(),v.appendChild(x);let b=document.createElement("span");b.className="dtk-ui-progresschip",b.style.display="none",v.appendChild(b);function k(d,y){if(y===0||d>=y){b.style.display="none";return}b.textContent=`${d}/${y}`,b.style.display=""}let g=document.createElement("button");g.type="button",g.className="dtk-ui-jobschip",g.innerHTML='<span class="dtk-ui-jobschip-dot"></span><span>idle</span>',g.onclick=()=>{E.isOpen()?E.close():Y()},v.appendChild(g);function S(){let d=o.jobs.find(ae=>ae.status==="running");g.classList.toggle("running",!!d);let y=d?`${d.kind} ${d.label}`:"idle",R=g.querySelector("span:last-child");R&&(R.textContent=y),g.title=d?`Started ${new Date(d.started_at).toLocaleString()}`:"No jobs running"}let N=document.createElement("div");N.className="dtk-ui-nextsteps-box",n.appendChild(N);function L(){Qt(N,o.nextSteps,{onLoad:d=>{_(d)},onTune:d=>{ie(d)},onFollow:d=>ee(d),onDismiss:()=>{o.nextSteps=null,L()}})}async function _(d){if(!Q){Q=!0;try{let y=await Ue({select:d,steps:["load","detect"],from:null,to:null,full_refresh:!1,force:!1});W("run",`run --select ${d}`,y.job_id,null),o.nextSteps&&o.nextSteps.metric===d&&(o.nextSteps={metric:d,jobId:y.job_id,phase:"running"},L())}catch(y){de(n,"error",y.message)}finally{Q=!1}}}function A(){let d=o.nextSteps;if(!d||!d.jobId||d.phase!=="running")return;let y=o.jobs.find(R=>R.id===d.jobId);!y||y.status==="running"||(o.nextSteps={...d,phase:y.status==="done"?"done":"failed"},L())}let I=document.createElement("div");I.className="dtk-ui-content",n.appendChild(I);function z(){var se,pe;I.innerHTML="";let d=r.length;if(d===0){let H=document.createElement("div");H.className="dtk-ui-empty",H.textContent="No metrics found for this project/selector.",I.appendChild(H);return}let y=o.metrics.filter(H=>!H.pending),R=y.filter(H=>H.error!==null);if(y.length===d&&R.length===d){let H=document.createElement("div");H.className="dtk-ui-banner";let Ee=(pe=(se=R[0])==null?void 0:se.error)!=null?pe:"unknown error";H.innerHTML=`<span>Failed to load overview: every metric failed (${O(Ee)}).</span>`;let be=document.createElement("button");be.type="button",be.className="dtk-ui-banner-retry",be.textContent="Retry",be.onclick=()=>{P()},H.appendChild(be),I.appendChild(H)}I.appendChild(kt(o.metrics)),I.appendChild(xt(y,o.tagFilter,H=>{o.tagFilter=H,z()}));let ae=o.tagFilter===null?o.metrics:o.metrics.filter(H=>o.tagFilter===Re?H.tags.length===0:H.tags.includes(o.tagFilter)),re=Nt(ae,o.sort,Date.now(),{onOpen:K,onTune:H=>{ie(H)},onRun:H=>T(H),onEdit:H=>{U(H)},onSortChange:H=>{o.sort=o.sort.key===H?{key:H,dir:o.sort.dir==="asc"?"desc":"asc"}:{key:H,dir:Ke[H]},z()}});I.appendChild(re.el),re.paint()}function K(d){q()&&(a&&a.close(),a=Rt(n,d,o.windowPreset,{onTune:y=>{ie(y)},onClose:()=>{a=null}}))}function q(){return s?s.requestClose():!0}function G(){q()&&(a&&(a.close(),a=null),s=et(n,{mode:"create",text:Wt,meta:i},{onSaved:(d,y)=>B(d,y),onDeleted:()=>{},onClose:()=>{s=null}}))}async function U(d){var R,ae;if(!q())return;let y;try{y=await pt(d)}catch(re){de(n,"error",re.message);return}q()&&(a&&(a.close(),a=null),s=et(n,{mode:"edit",name:y.name,file:y.file,text:y.text,digest:y.digest,data:(R=y.data)!=null?R:null,parseError:(ae=y.parse_error)!=null?ae:null,meta:i},{onSaved:(re,se)=>B(re,se),onDeleted:re=>le(re),onClose:()=>{s=null}}))}function B(d,y){de(n,"info",`Metric '${d.name}' ${y==="create"?"created":"saved"}.`),d.note&&de(n,"info",d.note),y==="create"&&(o.nextSteps={metric:d.name,jobId:null,phase:"idle"},L()),y==="edit"&&!d.renamed_from?J(d.name,d.metrics):ne(d.metrics)}function le(d){de(n,"info",`Metric '${d.name}' deleted (archived).`),d.note&&de(n,"info",d.note),ne(d.metrics)}function ne(d){r=d,V.refreshOptions(),P()}function J(d,y){r=y,V.refreshOptions();let R=y.find(se=>se.name===d),ae=o.metrics.findIndex(se=>se.name===d);if(!R||ae===-1){P();return}let re=C;o.metrics[ae]=Se(R),z(),Je(d,o.windowPreset).then(se=>{if(re!==C)return;se.pending=!1;let pe=o.metrics.findIndex(H=>H.name===d);pe!==-1&&(o.metrics[pe]=se),z()}).catch(se=>{if(re!==C)return;let pe=o.metrics.findIndex(H=>H.name===d);pe!==-1&&(o.metrics[pe]={...Se(R),pending:!1,error:se.message}),z()})}let V=Dt(n,{submitRun:d=>{me(d)},submitAutotune:d=>{D(d)},submitUnlock:d=>{X(d)},getSelectOptions:p,isPipelineBusy:h}),E=$t(n,{onFollow:d=>ee(d),onStop:d=>{F(d)}});function T(d){E.close(),V.refreshOptions(),V.refreshBusyState(),V.open(d)}function Y(){V.close(),E.render(o.jobs,Date.now(),o.followedJobId),E.open(),fe()}function W(d,y,R,ae){let re={id:R,kind:d,label:y,status:"running",returncode:null,url:ae,started_at:Date.now(),finished_at:null};o.jobs=[re,...o.jobs.filter(se=>se.id!==R)],ce(),fe(),te()}let Q=!1,oe=new Set;async function me(d){if(!Q){Q=!0;try{let y=await Ue(d);W("run",`run --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function D(d){if(!Q){Q=!0;try{let y=await lt(d);W("autotune",`autotune --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function X(d){if(!Q){Q=!0;try{let y=await dt(d);W("unlock",`unlock --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function ie(d){if(!oe.has(d)){oe.add(d),de(n,"info",`Opening tuner for ${d}\u2026`);try{let y=await ct({metric:d});W("tune",`tune --select ${d}`,y.job_id,y.url),window.open(y.url,"_blank")}catch(y){de(n,"error",y.message)}finally{oe.delete(d)}}}async function F(d){try{await ut(d),de(n,"info","Stop requested."),te()}catch(y){de(n,"error",y.message)}}function ee(d){o.followedJobId=d,o.followOffset=0,T(),V.resetLog(),he(d)}function ce(){S(),E.render(o.jobs,Date.now(),o.followedJobId),V.refreshBusyState(),A()}async function te(){try{let d=await at();o.jobs=d.jobs}catch{}ce()}function fe(){if(f!==void 0)return;let d=()=>{te().then(()=>{f=E.isOpen()||o.jobs.some(R=>R.status==="running")?window.setTimeout(d,2e3):void 0})};f=window.setTimeout(d,2e3)}function we(){l!==void 0&&(window.clearTimeout(l),l=void 0)}function he(d){we();let y=()=>{st(d,o.followOffset).then(R=>{if(o.followedJobId===d){if(V.appendLog(R.lines),o.followOffset=R.next_offset,R.status!=="running"){V.setLogStatus(R.status,R.returncode),l=void 0,te();return}l=window.setTimeout(y,1e3)}}).catch(R=>{de(n,"error",`job ${d}: ${R.message}`),l=void 0})};l=window.setTimeout(y,0)}let C=0;async function P(){let d=++C,y=o.windowPreset,R=r;if(o.metrics=R.map(Se),M.classList.add("spinning"),k(0,R.length),z(),R.length===0){M.classList.remove("spinning"),k(0,0);return}let ae=[...R],re=0;async function se(){for(;;){if(d!==C)return;let H=ae.shift();if(!H)return;let Ee;try{Ee=await Je(H.name,y),Ee.pending=!1}catch(qe){Ee={...Se(H),pending:!1,error:qe.message}}if(d!==C)return;let be=o.metrics.findIndex(qe=>qe.name===H.name);be!==-1&&(o.metrics[be]=Ee),re++,k(re,R.length),z()}}let pe=Math.min(qn,R.length);await Promise.all(Array.from({length:pe},()=>se())),d===C&&(M.classList.remove("spinning"),k(re,R.length),V.refreshOptions())}z(),V.refreshOptions(),P(),te()}window.__DTK_UI__={render:zn};})();
510
+ ${t.parseError}`:"Builder unavailable for this file"),u.onclick=()=>{oe("builder")};let x=document.createElement("button");x.type="button",x.className="dtk-ui-edtab",x.textContent="YAML",x.onclick=()=>{oe("yaml")},M.append(u,x),c.appendChild(M);let b=document.createElement("button");b.type="button",b.className="dtk-ui-overlay-close",b.textContent="\u2715",b.title="Close (Esc)",b.onclick=()=>{fe()},c.appendChild(b),m.appendChild(c);let k=document.createElement("div");k.className="dtk-ui-editor-body",m.appendChild(k);let g=null;if(r){let C=document.createElement("div");C.className="dtk-ui-editor-field",C.innerHTML='<span class="dtk-ui-field-label">Folder</span>',g=document.createElement("input"),g.type="text",g.className="dtk-ui-input",g.placeholder="optional subfolder under metrics/",C.appendChild(g);let A=document.createElement("div");A.className="dtk-ui-editor-hint",A.textContent="The file is written as metrics/[folder/]<name>.yml \u2014 <name> comes from the metric name below.",C.appendChild(A),k.appendChild(C)}let S=!r&&/(^|\n)\s*#/.test(t.text),N=document.createElement("div");N.className="dtk-ui-editor-note",N.textContent="Saving from Builder re-writes the YAML \u2014 comments are dropped (the previous file is archived to metrics/.history/).",N.style.display="none",k.appendChild(N);let L=document.createElement("div");L.className="dtk-ui-editor-panes",k.appendChild(L);let _=document.createElement("textarea");_.className="dtk-ui-editor-textarea",_.spellcheck=!1,_.autocapitalize="off",_.value=t.text,_.addEventListener("input",()=>{a=!0,l=!0,T()}),_.addEventListener("keydown",C=>{if(C.key==="Tab"){C.preventDefault();let A=_.selectionStart,G=_.selectionEnd;_.value=`${_.value.slice(0,A)} ${_.value.slice(G)}`,_.selectionStart=_.selectionEnd=A+2,a=!0,l=!0,T()}});let P=null;o&&(P=Kt(r?null:t.data,i,{onDirty:()=>{a=!0,f=!0,T()},osiInspect:C=>gt(C),osiImport:C=>ht(C)},{mode:t.mode}),L.appendChild(P.el)),L.appendChild(_),m.addEventListener("keydown",C=>{(C.ctrlKey||C.metaKey)&&C.key.toLowerCase()==="s"&&(C.preventDefault(),ee())});let I=document.createElement("div");I.className="dtk-ui-editor-error",I.style.display="none",m.appendChild(I);function z(C){I.textContent=C,I.style.display=""}function K(){I.style.display="none",I.textContent=""}let q=document.createElement("div");q.className="dtk-ui-editor-foot";let X=document.createElement("div");X.className="dtk-ui-editor-foot-left";let U=document.createElement("div");U.className="dtk-ui-editor-foot-right",q.append(X,U),m.appendChild(q);let B=document.createElement("span");B.className="dtk-ui-validchip",B.style.display="none",U.appendChild(B);let le=document.createElement("button");le.type="button",le.className="dtk-ui-btn primary";let ne=r?"Create metric":"Save changes";le.textContent=ne,le.onclick=()=>{ee()},U.appendChild(le);let J,V=0;function E(C,A,G){B.className=`dtk-ui-validchip ${C}`,B.innerHTML='<span class="dtk-ui-validchip-dot"></span>',B.appendChild(document.createTextNode(A)),B.title=G!=null?G:A,B.style.display=""}function T(){J!==void 0&&window.clearTimeout(J),J=window.setTimeout(()=>{J=void 0,Y()},600)}async function Y(){let C=++V,A;if(p==="builder"&&P){let G=P.clientIssues();if(G.length>0){E("warn",G[0],G.join(`
511
+ `));return}A=P.toYaml()}else A=_.value;E("checking","checking\u2026");try{if(await Ye(A),C!==V)return;E("ok","\u2713 valid config")}catch(G){if(C!==V)return;let d=G.message;E("err",d.split(`
512
+ `)[0],d)}}function W(){u.classList.toggle("on",p==="builder"),x.classList.toggle("on",p==="yaml"),P&&(P.el.style.display=p==="builder"?"":"none"),_.style.display=p==="yaml"?"":"none",N.style.display=p==="builder"&&S?"":"none"}let Q=!1;async function oe(C){if(!(C===p||s||Q)&&!(C==="builder"&&!P)){Q=!0;try{await me(C)}finally{Q=!1}}}async function me(C){if(C==="yaml")f&&P&&(_.value=P.toYaml(),f=!1,l=!1);else if(l&&P){K();try{let A=await Ye(_.value);P.setFromData(A.data),l=!1,f=!1}catch(A){if(!window.confirm(`The YAML doesn't validate:
513
+ ${A.message}
514
+
515
+ Return to the Builder anyway? Your YAML edits will be discarded (the Builder keeps its own last state).`)){z(`Fix the YAML (or discard it via the Builder tab):
516
+ ${A.message}`);return}l=!1,f=!0}}K(),p=C,W(),T()}function D(){let C=document.createElement("button");return C.type="button",C.className="dtk-ui-btn danger",C.textContent="Delete metric\u2026",C.onclick=()=>Z(),C}r||X.appendChild(D());function Z(){var ae,re;let C=(ae=t.name)!=null?ae:"",A=(re=t.file)!=null?re:"";X.innerHTML="";let G=document.createElement("div");G.className="dtk-ui-editor-confirm";let d=document.createElement("span");d.className="dtk-ui-editor-confirm-text",d.innerHTML=`Delete <b>${O(C)}</b>? The file <code>${O(A)}</code> is archived to <code>metrics/.history/</code> and removed. Data rows in the <code>_dtk_*</code> tables remain until <code>dtk clean</code>.`,G.appendChild(d);let y=document.createElement("button");y.type="button",y.className="dtk-ui-btn danger",y.textContent="Delete metric",y.onclick=()=>{ce(C,y,R)};let R=document.createElement("button");R.type="button",R.className="dtk-ui-btn",R.textContent="Cancel",R.onclick=()=>{X.innerHTML="",X.appendChild(D())},G.append(y,R),X.appendChild(G)}function ie(C){s=C,le.disabled=C,le.textContent=C?"Saving\u2026":ne}function F(){return p==="builder"&&P&&f?P.toYaml():_.value}async function ee(){var C;if(!s){if(K(),p==="builder"&&P){let A=P.clientIssues();if(A.length>0){z(`Fix before saving:
517
+ - ${A.join(`
518
+ - `)}`);return}}ie(!0);try{let A=F(),G=r?await mt({text:A,folder:(g==null?void 0:g.value.trim())||void 0}):await ft((C=t.name)!=null?C:"",{text:A,digest:t.digest});a=!1,n.onSaved(G,t.mode),te()}catch(A){z(A.message)}finally{ie(!1)}}}async function ce(C,A,G){if(!s){K(),s=!0,A.disabled=!0,G.disabled=!0,A.textContent="Deleting\u2026";try{let d=await bt(C);a=!1,n.onDeleted(d),te()}catch(d){z(d.message),A.disabled=!1,G.disabled=!1,A.textContent="Delete metric"}finally{s=!1}}}function te(){h.close(),n.onClose()}function fe(){return a&&!window.confirm("Discard unsaved changes?")?!1:(te(),!0)}return W(),p==="builder"&&P?P.focusFirst():_.focus(),T(),{close:te,requestClose:fe}}function Qt(e,t,n){if(e.innerHTML="",!t)return;let r=document.createElement("div");r.className="dtk-ui-nextsteps"+(t.phase==="failed"?" failed":"");let i=document.createElement("span");i.className="dtk-ui-nextsteps-text";let o=document.createElement("b");if(o.textContent=t.metric,t.phase==="done")i.append("Data loaded for ",o," \u2014 tune the detector on the real series.");else if(t.phase==="running"){i.append("Loading data for ",o,"\u2026 ");let p=document.createElement("a");p.href="#",p.className="dtk-ui-nextsteps-link",p.textContent="view log",p.onclick=h=>{h.preventDefault(),t.jobId&&n.onFollow(t.jobId)},i.appendChild(p)}else t.phase==="failed"?i.append("Loading data for ",o," failed \u2014 check the log, then retry."):i.append("Metric ",o," created. Next: load data, then tune the detector on it.");r.appendChild(i);let a=document.createElement("div");a.className="dtk-ui-nextsteps-actions";let s=document.createElement("button");if(s.type="button",s.className="dtk-ui-btn"+(t.phase==="idle"||t.phase==="failed"?" primary":""),s.textContent=t.phase==="failed"?"Retry load":"Load & detect",s.disabled=t.phase==="running",s.title="Runs `dtk run --steps load,detect` for this metric (no alerts sent)",s.onclick=()=>n.onLoad(t.metric),a.appendChild(s),t.phase==="running"){let p=document.createElement("span");p.className="dtk-ui-nextsteps-spin",a.appendChild(p)}let f=document.createElement("button");f.type="button",f.className="dtk-ui-btn"+(t.phase==="done"?" primary":""),f.textContent="Open tune",f.disabled=t.phase!=="done",f.title=t.phase==="done"?"Open the tune cockpit on the loaded series":"Load data first \u2014 the tune cockpit works on persisted datapoints",f.onclick=()=>n.onTune(t.metric),a.appendChild(f);let l=document.createElement("button");l.type="button",l.className="dtk-ui-nextsteps-close",l.textContent="\u2715",l.title="Dismiss",l.onclick=()=>n.onDismiss(),a.appendChild(l),r.appendChild(a),e.appendChild(r)}var qn=3,Fn=[{value:"24h",label:"24h"},{value:"7d",label:"7d"},{value:"30d",label:"30d"},{value:"90d",label:"90d"},{value:"all",label:"All"}];function zn(e,t){var G;yt(),t.classList.add(ve),t.innerHTML="";let n=document.createElement("div");n.className=`${ve}-root`,t.appendChild(n);let r=e.metrics,i=(G=e.form_meta)!=null?G:{channels:[],profiles:[],default_profile:null},o={windowPreset:e.initial_window||"30d",metrics:r.map(Se),jobs:[],followedJobId:null,followOffset:0,tagFilter:null,sort:{key:"alerts",dir:Ke.alerts},nextSteps:null},a=null,s=null,f,l;function p(){let d=new Set,y=new Set;for(let R of r){d.add(R.name);for(let ae of R.tags)y.add(ae)}return[...d,...[...y].map(R=>`tag:${R}`)]}function h(){let d=o.jobs.find(y=>y.status==="running"&&y.kind!=="tune");return d?{busy:!0,reason:`a pipeline job is already running (${d.label})`}:{busy:!1,reason:""}}let m=document.createElement("div");m.className="dtk-ui-header",n.appendChild(m);let c=document.createElement("div");c.className="dtk-ui-brand",c.innerHTML=`<span class="dtk-ui-brand-dot"></span><span class="dtk-ui-brand-name">detectkit \xB7 <b>${O(e.project)}</b></span>`,m.appendChild(c);let v=document.createElement("div");v.className="dtk-ui-header-right",m.appendChild(v);let w=document.createElement("div");w.className="dtk-ui-seg";for(let d of Fn){let y=document.createElement("button");y.type="button",y.className="dtk-ui-seg-btn"+(o.windowPreset===d.value?" on":""),y.textContent=d.label,y.onclick=()=>{o.windowPreset!==d.value&&(o.windowPreset=d.value,w.querySelectorAll(".dtk-ui-seg-btn").forEach(R=>R.classList.remove("on")),y.classList.add("on"),a&&a.setWindow(d.value),A())},w.appendChild(y)}v.appendChild(w);let M=document.createElement("button");M.type="button",M.className="dtk-ui-iconbtn",M.title="Refresh overview",M.textContent="\u27F3",M.onclick=()=>{A()},v.appendChild(M);let u=document.createElement("button");u.type="button",u.className="dtk-ui-runbtn",u.textContent="Run pipeline",u.onclick=()=>T(),v.appendChild(u);let x=document.createElement("button");x.type="button",x.className="dtk-ui-newbtn",x.textContent="New metric",x.onclick=()=>X(),v.appendChild(x);let b=document.createElement("span");b.className="dtk-ui-progresschip",b.style.display="none",v.appendChild(b);function k(d,y){if(y===0||d>=y){b.style.display="none";return}b.textContent=`${d}/${y}`,b.style.display=""}let g=document.createElement("button");g.type="button",g.className="dtk-ui-jobschip",g.innerHTML='<span class="dtk-ui-jobschip-dot"></span><span>idle</span>',g.onclick=()=>{E.isOpen()?E.close():Y()},v.appendChild(g);function S(){let d=o.jobs.find(ae=>ae.status==="running");g.classList.toggle("running",!!d);let y=d?`${d.kind} ${d.label}`:"idle",R=g.querySelector("span:last-child");R&&(R.textContent=y),g.title=d?`Started ${new Date(d.started_at).toLocaleString()}`:"No jobs running"}let N=document.createElement("div");N.className="dtk-ui-nextsteps-box",n.appendChild(N);function L(){Qt(N,o.nextSteps,{onLoad:d=>{_(d)},onTune:d=>{ie(d)},onFollow:d=>ee(d),onDismiss:()=>{o.nextSteps=null,L()}})}async function _(d){if(!Q){Q=!0;try{let y=await Ue({select:d,steps:["load","detect"],from:null,to:null,full_refresh:!1,force:!1});W("run",`run --select ${d}`,y.job_id,null),o.nextSteps&&o.nextSteps.metric===d&&(o.nextSteps={metric:d,jobId:y.job_id,phase:"running"},L())}catch(y){de(n,"error",y.message)}finally{Q=!1}}}function P(){let d=o.nextSteps;if(!d||!d.jobId||d.phase!=="running")return;let y=o.jobs.find(R=>R.id===d.jobId);!y||y.status==="running"||(o.nextSteps={...d,phase:y.status==="done"?"done":"failed"},L())}let I=document.createElement("div");I.className="dtk-ui-content",n.appendChild(I);function z(){var se,pe;I.innerHTML="";let d=r.length;if(d===0){let H=document.createElement("div");H.className="dtk-ui-empty",H.textContent="No metrics found for this project/selector.",I.appendChild(H);return}let y=o.metrics.filter(H=>!H.pending),R=y.filter(H=>H.error!==null);if(y.length===d&&R.length===d){let H=document.createElement("div");H.className="dtk-ui-banner";let Ee=(pe=(se=R[0])==null?void 0:se.error)!=null?pe:"unknown error";H.innerHTML=`<span>Failed to load overview: every metric failed (${O(Ee)}).</span>`;let be=document.createElement("button");be.type="button",be.className="dtk-ui-banner-retry",be.textContent="Retry",be.onclick=()=>{A()},H.appendChild(be),I.appendChild(H)}I.appendChild(kt(o.metrics)),I.appendChild(xt(y,o.tagFilter,H=>{o.tagFilter=H,z()}));let ae=o.tagFilter===null?o.metrics:o.metrics.filter(H=>o.tagFilter===Re?H.tags.length===0:H.tags.includes(o.tagFilter)),re=Nt(ae,o.sort,Date.now(),{onOpen:K,onTune:H=>{ie(H)},onRun:H=>T(H),onEdit:H=>{U(H)},onSortChange:H=>{o.sort=o.sort.key===H?{key:H,dir:o.sort.dir==="asc"?"desc":"asc"}:{key:H,dir:Ke[H]},z()}});I.appendChild(re.el),re.paint()}function K(d){q()&&(a&&a.close(),a=Rt(n,d,o.windowPreset,{onTune:y=>{ie(y)},onClose:()=>{a=null}}))}function q(){return s?s.requestClose():!0}function X(){q()&&(a&&(a.close(),a=null),s=et(n,{mode:"create",text:Wt,meta:i},{onSaved:(d,y)=>B(d,y),onDeleted:()=>{},onClose:()=>{s=null}}))}async function U(d){var R,ae;if(!q())return;let y;try{y=await pt(d)}catch(re){de(n,"error",re.message);return}q()&&(a&&(a.close(),a=null),s=et(n,{mode:"edit",name:y.name,file:y.file,text:y.text,digest:y.digest,data:(R=y.data)!=null?R:null,parseError:(ae=y.parse_error)!=null?ae:null,meta:i},{onSaved:(re,se)=>B(re,se),onDeleted:re=>le(re),onClose:()=>{s=null}}))}function B(d,y){de(n,"info",`Metric '${d.name}' ${y==="create"?"created":"saved"}.`),d.note&&de(n,"info",d.note),y==="create"&&(o.nextSteps={metric:d.name,jobId:null,phase:"idle"},L()),y==="edit"&&!d.renamed_from?J(d.name,d.metrics):ne(d.metrics)}function le(d){de(n,"info",`Metric '${d.name}' deleted (archived).`),d.note&&de(n,"info",d.note),ne(d.metrics)}function ne(d){r=d,V.refreshOptions(),A()}function J(d,y){r=y,V.refreshOptions();let R=y.find(se=>se.name===d),ae=o.metrics.findIndex(se=>se.name===d);if(!R||ae===-1){A();return}let re=C;o.metrics[ae]=Se(R),z(),Je(d,o.windowPreset).then(se=>{if(re!==C)return;se.pending=!1;let pe=o.metrics.findIndex(H=>H.name===d);pe!==-1&&(o.metrics[pe]=se),z()}).catch(se=>{if(re!==C)return;let pe=o.metrics.findIndex(H=>H.name===d);pe!==-1&&(o.metrics[pe]={...Se(R),pending:!1,error:se.message}),z()})}let V=Dt(n,{submitRun:d=>{me(d)},submitAutotune:d=>{D(d)},submitUnlock:d=>{Z(d)},getSelectOptions:p,isPipelineBusy:h}),E=$t(n,{onFollow:d=>ee(d),onStop:d=>{F(d)}});function T(d){E.close(),V.refreshOptions(),V.refreshBusyState(),V.open(d)}function Y(){V.close(),E.render(o.jobs,Date.now(),o.followedJobId),E.open(),fe()}function W(d,y,R,ae){let re={id:R,kind:d,label:y,status:"running",returncode:null,url:ae,started_at:Date.now(),finished_at:null};o.jobs=[re,...o.jobs.filter(se=>se.id!==R)],ce(),fe(),te()}let Q=!1,oe=new Set;async function me(d){if(!Q){Q=!0;try{let y=await Ue(d);W("run",`run --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function D(d){if(!Q){Q=!0;try{let y=await lt(d);W("autotune",`autotune --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function Z(d){if(!Q){Q=!0;try{let y=await dt(d);W("unlock",`unlock --select ${d.select}`,y.job_id,null),ee(y.job_id)}catch(y){de(n,"error",y.message)}finally{Q=!1}}}async function ie(d){if(!oe.has(d)){oe.add(d),de(n,"info",`Opening tuner for ${d}\u2026`);try{let y=await ct({metric:d});W("tune",`tune --select ${d}`,y.job_id,y.url),window.open(y.url,"_blank")}catch(y){de(n,"error",y.message)}finally{oe.delete(d)}}}async function F(d){try{await ut(d),de(n,"info","Stop requested."),te()}catch(y){de(n,"error",y.message)}}function ee(d){o.followedJobId=d,o.followOffset=0,T(),V.resetLog(),he(d)}function ce(){S(),E.render(o.jobs,Date.now(),o.followedJobId),V.refreshBusyState(),P()}async function te(){try{let d=await at();o.jobs=d.jobs}catch{}ce()}function fe(){if(f!==void 0)return;let d=()=>{te().then(()=>{f=E.isOpen()||o.jobs.some(R=>R.status==="running")?window.setTimeout(d,2e3):void 0})};f=window.setTimeout(d,2e3)}function we(){l!==void 0&&(window.clearTimeout(l),l=void 0)}function he(d){we();let y=()=>{st(d,o.followOffset).then(R=>{if(o.followedJobId===d){if(V.appendLog(R.lines),o.followOffset=R.next_offset,R.status!=="running"){V.setLogStatus(R.status,R.returncode),l=void 0,te();return}l=window.setTimeout(y,1e3)}}).catch(R=>{de(n,"error",`job ${d}: ${R.message}`),l=void 0})};l=window.setTimeout(y,0)}let C=0;async function A(){let d=++C,y=o.windowPreset,R=r;if(o.metrics=R.map(Se),M.classList.add("spinning"),k(0,R.length),z(),R.length===0){M.classList.remove("spinning"),k(0,0);return}let ae=[...R],re=0;async function se(){for(;;){if(d!==C)return;let H=ae.shift();if(!H)return;let Ee;try{Ee=await Je(H.name,y),Ee.pending=!1}catch(qe){Ee={...Se(H),pending:!1,error:qe.message}}if(d!==C)return;let be=o.metrics.findIndex(qe=>qe.name===H.name);be!==-1&&(o.metrics[be]=Ee),re++,k(re,R.length),z()}}let pe=Math.min(qn,R.length);await Promise.all(Array.from({length:pe},()=>se())),d===C&&(M.classList.remove("spinning"),k(re,R.length),V.refreshOptions())}z(),V.refreshOptions(),A(),te()}window.__DTK_UI__={render:zn};})();
@@ -841,10 +841,16 @@ class _Handler(BaseHTTPRequestHandler):
841
841
  error), so the detail rides in the body instead. Errors other than the
842
842
  routine bad-token 403 are also echoed to the terminal — a page stuck on
843
843
  a failing request should be diagnosable without opening devtools.
844
+ ``/api/metric-parse`` 400s are exempt too: that route powers the
845
+ editor's *live draft validation*, so "invalid config" is its routine,
846
+ per-keystroke outcome (surfaced in the page's validation chip), not an
847
+ operational error worth a terminal line.
844
848
  """
845
- if code >= 400 and code != 403:
849
+ path = urlparse(self.path).path
850
+ routine = code == 403 or (code == 400 and path == "/api/metric-parse")
851
+ if code >= 400 and not routine:
846
852
  first_line = detail.splitlines()[0] if detail else ""
847
- self._srv().echo(f" [ui] {code} {urlparse(self.path).path}: {first_line}")
853
+ self._srv().echo(f" [ui] {code} {path}: {first_line}")
848
854
  body = detail.encode("utf-8")
849
855
  self.send_response(code)
850
856
  self.send_header("Content-Type", "text/plain; charset=utf-8")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.55.0
3
+ Version: 0.55.1
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes