@64mb/freelens-power 1.0.3 → 1.1.0

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 (47) hide show
  1. package/README.md +5 -1
  2. package/out/main/common/monitoring-history.js +52 -0
  3. package/out/main/common/monitoring-history.js.map +1 -0
  4. package/out/main/common/store/monitoring-preferences-store.js +25 -2
  5. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  6. package/out/main/dashboard-views.js +132 -28
  7. package/out/main/dashboard-views.js.map +1 -1
  8. package/out/main/index.js +2 -0
  9. package/out/main/index.js.map +1 -1
  10. package/out/main/monitoring-history-database.js +103 -0
  11. package/out/main/monitoring-history-database.js.map +1 -0
  12. package/out/main/monitoring-history-worker.js +61 -0
  13. package/out/main/monitoring-history-worker.js.map +1 -0
  14. package/out/main/monitoring-history.js +100 -0
  15. package/out/main/monitoring-history.js.map +1 -0
  16. package/out/renderer/_virtual/_global-external_mobx.js +2 -1
  17. package/out/renderer/diagnostics.js +647 -0
  18. package/out/renderer/diagnostics.js.map +1 -0
  19. package/out/renderer/index.js +28 -1
  20. package/out/renderer/index.js.map +1 -1
  21. package/out/renderer/monitoring-collector.js +202 -0
  22. package/out/renderer/monitoring-collector.js.map +1 -0
  23. package/out/renderer/monitoring-history-collection.js +184 -0
  24. package/out/renderer/monitoring-history-collection.js.map +1 -0
  25. package/out/renderer/monitoring-history-view.js +150 -0
  26. package/out/renderer/monitoring-history-view.js.map +1 -0
  27. package/out/renderer/monitoring-history.js +19 -0
  28. package/out/renderer/monitoring-history.js.map +1 -0
  29. package/out/renderer/monitoring.js +98 -125
  30. package/out/renderer/monitoring.js.map +1 -1
  31. package/out/renderer/node-monitoring.js +116 -0
  32. package/out/renderer/node-monitoring.js.map +1 -0
  33. package/out/renderer/pod-monitoring.js +39 -8
  34. package/out/renderer/pod-monitoring.js.map +1 -1
  35. package/out/renderer/preferences/monitoring-preferences.js +55 -10
  36. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  37. package/out/renderer/src/common/monitoring-history.js +15 -0
  38. package/out/renderer/src/common/monitoring-history.js.map +1 -0
  39. package/out/renderer/src/common/monitoring-math.js +98 -0
  40. package/out/renderer/src/common/monitoring-math.js.map +1 -0
  41. package/out/renderer/src/common/resource-diagnostics.js +227 -0
  42. package/out/renderer/src/common/resource-diagnostics.js.map +1 -0
  43. package/out/renderer/src/common/store/monitoring-preferences-store.js +25 -2
  44. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  45. package/out/renderer/welcome.js +3 -1
  46. package/out/renderer/welcome.js.map +1 -1
  47. package/package.json +1 -1
@@ -0,0 +1,647 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
3
+ const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
4
+ const require__global_external_react = require("./_virtual/_global-external_react.js");
5
+ const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
6
+ const require_ai_icon = require("./ai-icon.js");
7
+ const require_resource_diagnostics = require("./src/common/resource-diagnostics.js");
8
+ const require_monitoring_collector = require("./monitoring-collector.js");
9
+ const require_monitoring_history_view = require("./monitoring-history-view.js");
10
+ const require_monitoring = require("./monitoring.js");
11
+ const require_monitoring_history_collection = require("./monitoring-history-collection.js");
12
+ //#region src/renderer/diagnostics.tsx
13
+ var hidden = () => document.visibilityState === "hidden";
14
+ var activeId = () => require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
15
+ var groupKey = (group) => `${group.namespace}/${group.identity}`;
16
+ function resourceText(value, metric) {
17
+ if (value === void 0) return "Unavailable";
18
+ return metric === "cpu" ? `${value.toFixed(3)} cores` : `${(value / 1024 ** 2).toFixed(1)} MiB`;
19
+ }
20
+ function summaryText(summary, metric) {
21
+ const request = summary.unknownRequestPods ? `partial ${resourceText(summary.requests, metric)} known (${summary.unknownRequestPods} pods unknown)` : resourceText(summary.requests, metric);
22
+ const ratio = summary.usage !== void 0 && summary.requests > 0 && !summary.unknownRequestPods && summary.coveredPods === summary.totalPods ? `${(summary.usage / summary.requests * 100).toFixed(0)}% of requests` : "ratio unavailable";
23
+ return `${resourceText(summary.usage, metric)} (${summary.coveredPods}/${summary.totalPods} pods covered), requests ${request}, ${ratio}`;
24
+ }
25
+ function unscheduledText(summary, metric) {
26
+ return `${summary.unknownRequestPods ? "Partial known: " : ""}${resourceText(summary.unscheduledRequests, metric)}`;
27
+ }
28
+ var resources = {
29
+ Pod: "/api/v1",
30
+ Deployment: "/apis/apps/v1",
31
+ ReplicaSet: "/apis/apps/v1",
32
+ StatefulSet: "/apis/apps/v1",
33
+ DaemonSet: "/apis/apps/v1",
34
+ Job: "/apis/batch/v1",
35
+ CronJob: "/apis/batch/v1"
36
+ };
37
+ var plurals = {
38
+ Pod: "pods",
39
+ Deployment: "deployments",
40
+ ReplicaSet: "replicasets",
41
+ StatefulSet: "statefulsets",
42
+ DaemonSet: "daemonsets",
43
+ Job: "jobs",
44
+ CronJob: "cronjobs"
45
+ };
46
+ function openResource(object, clusterId) {
47
+ const kind = object.kind ?? "Pod";
48
+ if (activeId() !== clusterId || !object.metadata.name || !object.metadata.namespace || !resources[kind]) return;
49
+ const path = `${resources[kind]}/namespaces/${encodeURIComponent(object.metadata.namespace)}/${plurals[kind]}/${encodeURIComponent(object.metadata.name)}`;
50
+ Promise.resolve(require_extensions.Renderer.Navigation.showDetails(path)).catch(() => {});
51
+ }
52
+ var projections = /* @__PURE__ */ new WeakMap();
53
+ function projectSnapshot(data) {
54
+ const usage = require_monitoring_collector.getCollectedPodUsage(data);
55
+ const groups = require_resource_diagnostics.buildConsumerGroups(data.pods ?? [], data.owners ?? [], usage);
56
+ const objects = /* @__PURE__ */ new Map();
57
+ const children = /* @__PURE__ */ new Map();
58
+ for (const object of [...data.owners ?? [], ...data.pods ?? []]) {
59
+ const uid = object.metadata.uid;
60
+ if (!uid) continue;
61
+ objects.set(uid, object);
62
+ const refs = object.metadata.ownerReferences ?? [];
63
+ const parent = refs.find((item) => item.controller) ?? (refs.length === 1 ? refs[0] : void 0);
64
+ if (parent) {
65
+ const siblings = children.get(parent.uid) ?? [];
66
+ siblings.push(uid);
67
+ children.set(parent.uid, siblings);
68
+ }
69
+ }
70
+ const related = /* @__PURE__ */ new Map();
71
+ for (const group of groups) {
72
+ const seen = /* @__PURE__ */ new Set();
73
+ const stack = [group.identity];
74
+ while (stack.length) {
75
+ const uid = stack.pop();
76
+ if (seen.has(uid)) continue;
77
+ const object = objects.get(uid);
78
+ if (object && object.metadata.namespace !== group.namespace) continue;
79
+ seen.add(uid);
80
+ stack.push(...children.get(uid) ?? []);
81
+ }
82
+ related.set(groupKey(group), seen);
83
+ }
84
+ return {
85
+ usage,
86
+ groups,
87
+ objects,
88
+ related
89
+ };
90
+ }
91
+ function projection(data) {
92
+ let value = projections.get(data);
93
+ if (!value) {
94
+ value = projectSnapshot(data);
95
+ projections.set(data, value);
96
+ }
97
+ return value;
98
+ }
99
+ function DiagnosticsPage() {
100
+ const [tab, setTab] = require__global_external_react.useState("Consumers");
101
+ const [snapshot, setSnapshot] = require__global_external_react.useState();
102
+ const [error, setError] = require__global_external_react.useState("");
103
+ const [filter, setFilter] = require__global_external_react.useState("");
104
+ const [historyTarget, setHistoryTarget] = require__global_external_react.useState("");
105
+ const [groupMode, setGroupMode] = require__global_external_react.useState("workload");
106
+ const [namespacesOpen, setNamespacesOpen] = require__global_external_react.useState([]);
107
+ const known = require__global_external_react.useRef(/* @__PURE__ */ new Map());
108
+ const [sort, setSort] = require__global_external_react.useState("cpu");
109
+ const [expanded, setExpanded] = require__global_external_react.useState([]);
110
+ const [selected, setSelected] = require__global_external_react.useState("");
111
+ const [pendingUid, setPendingUid] = require__global_external_react.useState("");
112
+ const [reason, setReason] = require__global_external_react.useState("");
113
+ const [warningOnly, setWarningOnly] = require__global_external_react.useState(false);
114
+ const [observed, setObserved] = require__global_external_react.useState([]);
115
+ const previous = require__global_external_react.useRef();
116
+ require__global_external_react.useEffect(() => {
117
+ const release = require_monitoring_collector.retainMonitoringCollector();
118
+ let disposed = false;
119
+ let generation = 0;
120
+ let loading = false;
121
+ const clear = () => {
122
+ generation++;
123
+ previous.current = void 0;
124
+ known.current.clear();
125
+ setSnapshot(void 0);
126
+ setObserved([]);
127
+ setSelected("");
128
+ setPendingUid("");
129
+ setExpanded([]);
130
+ setError("");
131
+ };
132
+ const load = async () => {
133
+ if (loading || disposed || hidden()) return;
134
+ if (!activeId()) {
135
+ setError("Select a connected cluster");
136
+ return;
137
+ }
138
+ const current = generation;
139
+ loading = true;
140
+ try {
141
+ const next = await require_monitoring_collector.getMonitoringSnapshot(true);
142
+ if (disposed || current !== generation || next.clusterId !== activeId() || hidden()) return;
143
+ const prior = previous.current;
144
+ if (prior === next) return;
145
+ if (prior && prior.clusterId === next.clusterId) {
146
+ const entries = [];
147
+ if (prior.time !== next.time) entries.push(...require_resource_diagnostics.observeResourceChanges(prior.pods ?? [], next.pods ?? [], new Date(next.time).toISOString()));
148
+ if (prior.owners !== next.owners && next.diagnosticsTime) entries.push(...require_resource_diagnostics.observeResourceChanges(prior.owners ?? [], next.owners ?? [], new Date(next.diagnosticsTime).toISOString()));
149
+ if (entries.length) setObserved((items) => [...entries, ...items].slice(0, 300));
150
+ }
151
+ const projected = projection(next);
152
+ for (const group of projected.groups) {
153
+ const key = groupKey(group);
154
+ const remembered = known.current.get(key)?.uids ?? /* @__PURE__ */ new Set();
155
+ for (const uid of projected.related.get(key) ?? []) remembered.add(uid);
156
+ while (remembered.size > 500) remembered.delete(remembered.values().next().value);
157
+ known.current.set(key, {
158
+ group,
159
+ uids: remembered
160
+ });
161
+ }
162
+ while (known.current.size > 100) known.current.delete(known.current.keys().next().value);
163
+ previous.current = next;
164
+ setSnapshot(next);
165
+ setError("");
166
+ } catch {
167
+ if (!disposed && current === generation) setError("Diagnostics unavailable or access denied");
168
+ } finally {
169
+ loading = false;
170
+ }
171
+ };
172
+ const stop = require__global_external_mobx.reaction(activeId, () => {
173
+ clear();
174
+ load();
175
+ });
176
+ const visibility = () => {
177
+ if (document.visibilityState !== "hidden") load();
178
+ };
179
+ const timer = window.setInterval(() => void load(), 1e3);
180
+ document.addEventListener("visibilitychange", visibility);
181
+ load();
182
+ return () => {
183
+ disposed = true;
184
+ generation++;
185
+ stop();
186
+ release();
187
+ window.clearInterval(timer);
188
+ document.removeEventListener("visibilitychange", visibility);
189
+ };
190
+ }, []);
191
+ const data = snapshot?.clusterId === activeId() ? snapshot : void 0;
192
+ const projected = require__global_external_react.useMemo(() => data ? projection(data) : void 0, [data]);
193
+ const groups = projected?.groups ?? [];
194
+ const filtered = require__global_external_react.useMemo(() => groups.filter((group) => `${group.namespace} ${group.name} ${group.kind}`.toLowerCase().includes(filter.toLowerCase())).sort((a, b) => sort === "name" ? groupKey(a).localeCompare(groupKey(b)) : sort === "requests" || sort === "memoryRequests" ? b[sort === "requests" ? "cpu" : "memory"].requests - a[sort === "requests" ? "cpu" : "memory"].requests : (b[sort === "memory" ? "memory" : "cpu"].usage ?? -1) - (a[sort === "memory" ? "memory" : "cpu"].usage ?? -1)), [
195
+ groups,
196
+ filter,
197
+ sort
198
+ ]);
199
+ const applications = [...known.current.values()].map((item) => item.group);
200
+ const application = applications.find((group) => groupKey(group) === selected);
201
+ const uids = new Set(known.current.get(selected)?.uids ?? []);
202
+ const warningIds = new Set((data?.events ?? []).filter((event) => event.type === "Warning").map((event) => event.metadata?.uid));
203
+ const timeline = [...require_resource_diagnostics.normalizeEvents(data?.events ?? []), ...observed].filter((entry) => !!application && !!entry.objectUid && uids.has(entry.objectUid) && (!reason || `${entry.reason} ${entry.message}`.toLowerCase().includes(reason.toLowerCase())) && (!warningOnly || warningIds.has(entry.id))).sort((a, b) => (Date.parse(b.time ?? "") || 0) - (Date.parse(a.time ?? "") || 0));
204
+ const pending = (data?.pods ?? []).filter((pod) => pod.status?.phase === "Pending");
205
+ const selectedPod = pending.find((pod) => pod.metadata.uid === pendingUid);
206
+ const diagnosis = selectedPod ? require_resource_diagnostics.diagnosePendingPod(selectedPod, data?.nodes ?? [], data?.events ?? [], data?.pvcs) : void 0;
207
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
208
+ className: "power-diagnostics",
209
+ style: {
210
+ padding: 20,
211
+ overflow: "auto",
212
+ height: "100%"
213
+ },
214
+ children: [
215
+ /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: `.power-diagnostics table{width:100%;border-collapse:collapse}.power-diagnostics th,.power-diagnostics td{text-align:left;padding:9px;border-bottom:1px solid var(--borderColor);vertical-align:top}.power-diagnostics button,.power-diagnostics select,.power-diagnostics input{margin:4px;padding:6px}.power-diagnostics .toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.power-diagnostics small{display:block;opacity:.75}.power-diagnostics button{cursor:pointer;display:inline-flex;align-items:center;gap:6px;border-radius:4px;border:1px solid var(--borderColor);background:var(--contentColor);color:var(--textColorPrimary);font:inherit}.power-diagnostics button:hover{border-color:var(--primary)}.power-diagnostics button[aria-pressed=true]{color:var(--primary);border-color:var(--primary)}.power-diagnostics button:disabled{opacity:.5;cursor:default}.power-diagnostics select,.power-diagnostics input{border-radius:4px;border:1px solid var(--borderColor);background:var(--contentColor);color:var(--textColorPrimary);font:inherit}.power-diagnostics :is(button,select,input):focus-visible{outline:2px solid var(--primary);outline-offset:2px}.power-diagnostics input[type=checkbox]{accent-color:var(--primary)}.power-diagnostics th{color:var(--textColorSecondary);font-weight:500}.power-diagnostics nav{display:flex;gap:8px;flex-wrap:wrap}` }),
216
+ /* @__PURE__ */ require_jsx_runtime.jsx("h2", { children: "Resource diagnostics" }),
217
+ /* @__PURE__ */ require_jsx_runtime.jsx("nav", {
218
+ "aria-label": "Diagnostics views",
219
+ children: [
220
+ "Consumers",
221
+ "Timeline",
222
+ "Pending"
223
+ ].map((name) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
224
+ type: "button",
225
+ title: `Show ${name.toLowerCase()}`,
226
+ "aria-pressed": tab === name,
227
+ onClick: () => setTab(name),
228
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: name === "Consumers" ? "account_tree" : name === "Timeline" ? "timeline" : "hourglass_empty" }), name]
229
+ }, name))
230
+ }),
231
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", { children: ["Read-only cluster snapshot", data ? ` · ${new Date(data.time).toLocaleTimeString([], { hourCycle: "h23" })}` : " · Loading"] }),
232
+ error && /* @__PURE__ */ require_jsx_runtime.jsx("p", {
233
+ role: "status",
234
+ children: error
235
+ }),
236
+ data?.errors.map((message) => /* @__PURE__ */ require_jsx_runtime.jsx("p", {
237
+ role: "status",
238
+ children: message
239
+ }, message)),
240
+ tab === "Consumers" && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
241
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
242
+ className: "toolbar",
243
+ children: [
244
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
245
+ "Group by",
246
+ " ",
247
+ /* @__PURE__ */ require_jsx_runtime.jsxs("select", {
248
+ "aria-label": "Group consumers",
249
+ value: groupMode,
250
+ onChange: (event) => setGroupMode(event.target.value),
251
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("option", {
252
+ value: "workload",
253
+ children: "Workload"
254
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("option", {
255
+ value: "namespace",
256
+ children: "Namespace"
257
+ })]
258
+ })
259
+ ] }),
260
+ /* @__PURE__ */ require_jsx_runtime.jsx("input", {
261
+ "aria-label": "Filter consumers",
262
+ placeholder: "Namespace or workload",
263
+ value: filter,
264
+ onChange: (event) => setFilter(event.target.value)
265
+ }),
266
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
267
+ "Sort",
268
+ " ",
269
+ /* @__PURE__ */ require_jsx_runtime.jsxs("select", {
270
+ "aria-label": "Sort consumers",
271
+ value: sort,
272
+ onChange: (event) => setSort(event.target.value),
273
+ children: [
274
+ /* @__PURE__ */ require_jsx_runtime.jsx("option", {
275
+ value: "cpu",
276
+ children: "CPU usage"
277
+ }),
278
+ /* @__PURE__ */ require_jsx_runtime.jsx("option", {
279
+ value: "memory",
280
+ children: "Memory usage"
281
+ }),
282
+ /* @__PURE__ */ require_jsx_runtime.jsx("option", {
283
+ value: "requests",
284
+ children: "CPU requests"
285
+ }),
286
+ /* @__PURE__ */ require_jsx_runtime.jsx("option", {
287
+ value: "memoryRequests",
288
+ children: "Memory requests"
289
+ }),
290
+ /* @__PURE__ */ require_jsx_runtime.jsx("option", {
291
+ value: "name",
292
+ children: "Namespace / name"
293
+ })
294
+ ]
295
+ })
296
+ ] })
297
+ ]
298
+ }),
299
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Usage coverage is per resource. Missing metrics are unavailable. Unscheduled requests are listed separately." }),
300
+ /* @__PURE__ */ require_jsx_runtime.jsxs("table", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("thead", { children: /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
301
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Namespace / consumer" }),
302
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "CPU" }),
303
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Memory" }),
304
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Unscheduled requests" })
305
+ ] }) }), /* @__PURE__ */ require_jsx_runtime.jsx("tbody", { children: (groupMode === "namespace" ? [...new Set(filtered.map((group) => group.namespace))] : [""]).map((namespace) => /* @__PURE__ */ require_jsx_runtime.jsx(NamespaceRows, {
306
+ namespace,
307
+ groups: namespace ? filtered.filter((group) => group.namespace === namespace) : filtered,
308
+ clusterId: data?.clusterId ?? "",
309
+ usage: projected?.usage ?? [],
310
+ viewHistory: setHistoryTarget,
311
+ open: !namespace || namespacesOpen.includes(namespace),
312
+ toggleNamespace: () => setNamespacesOpen((items) => items.includes(namespace) ? items.filter((item) => item !== namespace) : [...items, namespace]),
313
+ expanded,
314
+ toggleGroup: (key) => setExpanded((items) => items.includes(key) ? items.filter((item) => item !== key) : [...items, key])
315
+ }, namespace)) })] }),
316
+ !filtered.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "No consumers available" }),
317
+ data && historyTarget && /* @__PURE__ */ require_jsx_runtime.jsx(ApplicationHistory, {
318
+ data,
319
+ group: groups.find((group) => groupKey(group) === historyTarget)
320
+ }, `${data.clusterId}/${historyTarget}`)
321
+ ] }),
322
+ tab === "Timeline" && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
323
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
324
+ className: "toolbar",
325
+ children: [
326
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
327
+ "Application",
328
+ " ",
329
+ /* @__PURE__ */ require_jsx_runtime.jsxs("select", {
330
+ "aria-label": "Timeline application",
331
+ value: selected,
332
+ onChange: (event) => setSelected(event.target.value),
333
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("option", {
334
+ value: "",
335
+ children: "Select a workload or standalone pod"
336
+ }), applications.map((group) => /* @__PURE__ */ require_jsx_runtime.jsxs("option", {
337
+ value: groupKey(group),
338
+ children: [
339
+ group.namespace,
340
+ " / ",
341
+ group.kind,
342
+ " / ",
343
+ group.name
344
+ ]
345
+ }, groupKey(group)))]
346
+ })
347
+ ] }),
348
+ /* @__PURE__ */ require_jsx_runtime.jsx("input", {
349
+ "aria-label": "Filter timeline reason",
350
+ placeholder: "Reason or message",
351
+ value: reason,
352
+ onChange: (event) => setReason(event.target.value)
353
+ }),
354
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
355
+ type: "checkbox",
356
+ checked: warningOnly,
357
+ onChange: (event) => setWarningOnly(event.target.checked)
358
+ }), "Warnings only"] })
359
+ ]
360
+ }),
361
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Kubernetes events have limited retention. Observed changes begin with this open view, retain at most 300 entries, and have unknown transition times between snapshots. Related identities observed in this view are retained for at most 100 applications and 500 identities per application; older identity coverage can be incomplete." }),
362
+ /* @__PURE__ */ require_jsx_runtime.jsxs("table", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("thead", { children: /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
363
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Time / certainty" }),
364
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Reason" }),
365
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Evidence" })
366
+ ] }) }), /* @__PURE__ */ require_jsx_runtime.jsx("tbody", { children: timeline.map((entry) => /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
367
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [entry.time ? new Date(entry.time).toLocaleString([], { hourCycle: "h23" }) : "Time unknown", /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
368
+ entry.certainty,
369
+ ", count ",
370
+ entry.count,
371
+ entry.firstTime ? `, first ${new Date(entry.firstTime).toLocaleString([], { hourCycle: "h23" })}` : ""
372
+ ] })] }),
373
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: entry.reason }),
374
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [
375
+ entry.message,
376
+ (() => {
377
+ const object = entry.objectUid ? projected?.objects.get(entry.objectUid) : void 0;
378
+ return object && data ? /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
379
+ type: "button",
380
+ title: "Open exact resource details",
381
+ onClick: () => openResource(object, data.clusterId),
382
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" }), "Open resource"]
383
+ }) : null;
384
+ })(),
385
+ /* @__PURE__ */ require_jsx_runtime.jsx("small", { children: entry.gap })
386
+ ] })
387
+ ] }, entry.id)) })] }),
388
+ !timeline.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: application ? "No matching retained events or observed changes" : "Select an application to inspect its exact resource identities" })
389
+ ] }),
390
+ tab === "Pending" && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: [
391
+ "Pending pod",
392
+ " ",
393
+ /* @__PURE__ */ require_jsx_runtime.jsxs("select", {
394
+ "aria-label": "Pending pod",
395
+ value: pendingUid,
396
+ onChange: (event) => setPendingUid(event.target.value),
397
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("option", {
398
+ value: "",
399
+ children: "Select a pod"
400
+ }), pending.map((pod) => /* @__PURE__ */ require_jsx_runtime.jsxs("option", {
401
+ value: pod.metadata.uid,
402
+ children: [
403
+ pod.metadata.namespace,
404
+ " / ",
405
+ pod.metadata.name
406
+ ]
407
+ }, pod.metadata.uid))]
408
+ })
409
+ ] }), selectedPod && diagnosis ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
410
+ /* @__PURE__ */ require_jsx_runtime.jsx("h3", { children: diagnosis.state === "assigned-waiting" ? "Assigned and waiting" : "Unscheduled" }),
411
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", { children: [
412
+ "Requested CPU: ",
413
+ podRequest(selectedPod, "cpu"),
414
+ " · Requested memory: ",
415
+ podRequest(selectedPod, "memory")
416
+ ] }),
417
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", { children: [
418
+ "Node selector:",
419
+ " ",
420
+ Object.entries(selectedPod.spec?.nodeSelector ?? {}).map(([key, value]) => `${key}=${value}`).join(", ") || "None"
421
+ ] }),
422
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", { children: [
423
+ "Tolerations:",
424
+ " ",
425
+ (selectedPod.spec?.tolerations ?? []).map((item) => `${item.key || "all keys"} ${item.operator ?? "Equal"}${item.value ? ` ${item.value}` : ""} (${item.effect || "all effects"})`).join(", ") || "None"
426
+ ] }),
427
+ /* @__PURE__ */ require_jsx_runtime.jsxs("p", { children: [
428
+ "Required node affinity:",
429
+ " ",
430
+ selectedPod.spec?.affinity?.nodeAffinity?.requiredDuringSchedulingIgnoredDuringExecution ? JSON.stringify(selectedPod.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution).slice(0, 2e3) : "None"
431
+ ] }),
432
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
433
+ type: "button",
434
+ title: "Open pod details",
435
+ onClick: () => openResource(selectedPod, data.clusterId),
436
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" }), "Open pod"]
437
+ }),
438
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: selectedPod.status?.conditions?.map((condition) => `${condition.type}: ${condition.status}${condition.message ? ` (${condition.message})` : ""}`).join(" · ") }),
439
+ /* @__PURE__ */ require_jsx_runtime.jsxs("table", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("thead", { children: /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
440
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Evidence source" }),
441
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Node" }),
442
+ /* @__PURE__ */ require_jsx_runtime.jsx("th", { children: "Finding" })
443
+ ] }) }), /* @__PURE__ */ require_jsx_runtime.jsx("tbody", { children: diagnosis.evidence.map((entry, index) => /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
444
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: entry.source === "event" ? "Reported Kubernetes event" : "Local hard constraint" }),
445
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: entry.node ?? "—" }),
446
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: entry.message })
447
+ ] }, `${entry.node}/${index}`)) })] }),
448
+ !diagnosis.evidence.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "No retained evidence is available for this pod" }),
449
+ diagnosis.limitations.map((message) => /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: message }, message))
450
+ ] }) : /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: pending.length ? "Select a pending pod" : "No pending pods available" })] })
451
+ ]
452
+ });
453
+ }
454
+ function ConsumerRows({ group, clusterId, expanded, toggle, usage, viewHistory }) {
455
+ return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
456
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [
457
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
458
+ type: "button",
459
+ title: expanded ? "Collapse pods" : "Expand pods",
460
+ "aria-expanded": expanded,
461
+ onClick: toggle,
462
+ children: [
463
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: expanded ? "expand_less" : "expand_more" }),
464
+ group.namespace,
465
+ " / ",
466
+ group.kind,
467
+ " / ",
468
+ group.name
469
+ ]
470
+ }),
471
+ /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
472
+ group.pods.length,
473
+ " pods",
474
+ group.ownerResolved ? "" : " · Owner chain incomplete"
475
+ ] }),
476
+ /* @__PURE__ */ require_jsx_runtime.jsx(TrackButton, { identity: {
477
+ clusterId,
478
+ scope: group.kind === "Pod" ? "pod" : "workload",
479
+ uid: group.identity
480
+ } }),
481
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
482
+ type: "button",
483
+ title: "View resource history",
484
+ onClick: () => viewHistory(groupKey(group)),
485
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "timeline" }), "View history"]
486
+ })
487
+ ] }),
488
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: summaryText(group.cpu, "cpu") }),
489
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: summaryText(group.memory, "memory") }),
490
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [
491
+ unscheduledText(group.cpu, "cpu"),
492
+ " / ",
493
+ unscheduledText(group.memory, "memory")
494
+ ] })
495
+ ] }), expanded && group.pods.map((pod) => /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
496
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [
497
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
498
+ type: "button",
499
+ title: "Open pod details",
500
+ onClick: () => openResource(pod, clusterId),
501
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "open_in_new" }), pod.metadata.name]
502
+ }),
503
+ " ",
504
+ pod.metadata.uid && /* @__PURE__ */ require_jsx_runtime.jsx(TrackButton, { identity: {
505
+ clusterId,
506
+ scope: "pod",
507
+ uid: pod.metadata.uid
508
+ } }),
509
+ pod.status?.phase ?? "Unknown",
510
+ " ·",
511
+ " ",
512
+ pod.spec?.nodeName ? `Assigned to ${pod.spec.nodeName}` : "Unscheduled"
513
+ ] }),
514
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: podSummary(pod, usage, "cpu") }),
515
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: podSummary(pod, usage, "memory") }),
516
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: pod.spec?.nodeName ? "—" : "Included above" })
517
+ ] }, pod.metadata.uid))] });
518
+ }
519
+ function TrackButton({ identity }) {
520
+ const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
521
+ const [, update] = require__global_external_react.useState(0);
522
+ require__global_external_react.useEffect(() => require__global_external_mobx.reaction(() => [preferences.historyEnabled, require_monitoring_history_collection.isHistoryTracked(identity)], () => update((value) => value + 1)), [
523
+ identity.clusterId,
524
+ identity.scope,
525
+ identity.uid,
526
+ preferences
527
+ ]);
528
+ const tracked = require_monitoring_history_collection.isHistoryTracked(identity);
529
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
530
+ type: "button",
531
+ disabled: !preferences.historyEnabled,
532
+ title: preferences.historyEnabled ? "Track numeric resource history locally" : "Enable local history in extension preferences",
533
+ "aria-pressed": tracked,
534
+ onClick: () => {
535
+ if (activeId() !== identity.clusterId || !preferences.historyEnabled) return;
536
+ require_monitoring_history_collection.setHistoryTracked(identity, !tracked);
537
+ update((value) => value + 1);
538
+ },
539
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: tracked ? "visibility" : "visibility_off" }), tracked ? "Stop tracking" : "Track history"]
540
+ });
541
+ }
542
+ function podRequest(pod, metric) {
543
+ try {
544
+ return resourceText(require_resource_diagnostics.effectivePodResource(pod, metric), metric);
545
+ } catch {
546
+ return "Unavailable";
547
+ }
548
+ }
549
+ function podSummary(pod, usage, metric) {
550
+ let request;
551
+ try {
552
+ request = require_resource_diagnostics.effectivePodResource(pod, metric);
553
+ } catch {}
554
+ const value = usage.find((item) => item.uid === pod.metadata.uid)?.[metric];
555
+ return `${resourceText(value, metric)}, requests ${resourceText(request, metric)}${value !== void 0 && request ? `, ${(value / request * 100).toFixed(0)}% of requests` : ", ratio unavailable"}`;
556
+ }
557
+ function NamespaceRows({ namespace, groups, clusterId, usage, open, toggleNamespace, expanded, toggleGroup, viewHistory }) {
558
+ const sum = (metric) => groups.reduce((total, group) => {
559
+ const next = group[metric];
560
+ return {
561
+ requests: total.requests + next.requests,
562
+ scheduledRequests: total.scheduledRequests + next.scheduledRequests,
563
+ unscheduledRequests: total.unscheduledRequests + next.unscheduledRequests,
564
+ usage: total.usage === void 0 && next.usage === void 0 ? void 0 : (total.usage ?? 0) + (next.usage ?? 0),
565
+ coveredPods: total.coveredPods + next.coveredPods,
566
+ totalPods: total.totalPods + next.totalPods,
567
+ unknownRequestPods: total.unknownRequestPods + next.unknownRequestPods
568
+ };
569
+ }, {
570
+ requests: 0,
571
+ scheduledRequests: 0,
572
+ unscheduledRequests: 0,
573
+ coveredPods: 0,
574
+ totalPods: 0,
575
+ unknownRequestPods: 0
576
+ });
577
+ return /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [namespace && /* @__PURE__ */ require_jsx_runtime.jsxs("tr", { children: [
578
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [/* @__PURE__ */ require_jsx_runtime.jsxs("button", {
579
+ type: "button",
580
+ title: "Expand namespace workloads",
581
+ "aria-expanded": open,
582
+ onClick: toggleNamespace,
583
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: open ? "expand_less" : "expand_more" }), namespace]
584
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [groups.length, " consumers"] })] }),
585
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: summaryText(sum("cpu"), "cpu") }),
586
+ /* @__PURE__ */ require_jsx_runtime.jsx("td", { children: summaryText(sum("memory"), "memory") }),
587
+ /* @__PURE__ */ require_jsx_runtime.jsxs("td", { children: [
588
+ unscheduledText(sum("cpu"), "cpu"),
589
+ " / ",
590
+ unscheduledText(sum("memory"), "memory")
591
+ ] })
592
+ ] }), open && groups.map((group) => /* @__PURE__ */ require_jsx_runtime.jsx(ConsumerRows, {
593
+ group,
594
+ clusterId,
595
+ expanded: expanded.includes(groupKey(group)),
596
+ toggle: () => toggleGroup(groupKey(group)),
597
+ usage,
598
+ viewHistory
599
+ }, groupKey(group)))] });
600
+ }
601
+ function ApplicationHistory({ data, group }) {
602
+ const live = require__global_external_react.useMemo(() => {
603
+ if (!group || group.cpu.coveredPods !== group.cpu.totalPods || group.memory.coveredPods !== group.memory.totalPods || group.cpu.usage === void 0 || group.memory.usage === void 0) return [];
604
+ const names = new Set(group.pods.map((pod) => `${pod.metadata.namespace}/${pod.metadata.name}`));
605
+ const times = (data.podMetrics ?? []).filter((metric) => names.has(`${metric.metadata.namespace}/${metric.metadata.name}`)).map((metric) => Date.parse(metric.timestamp));
606
+ if (!times.length || times.some((time) => !Number.isFinite(time))) return [];
607
+ return [{
608
+ time: Math.min(...times),
609
+ cpu: group.cpu.usage,
610
+ memory: group.memory.usage,
611
+ resources: {
612
+ cpu: { requests: group.cpu.unknownRequestPods ? void 0 : group.cpu.requests },
613
+ memory: { requests: group.memory.unknownRequestPods ? void 0 : group.memory.requests }
614
+ }
615
+ }];
616
+ }, [data, group]);
617
+ const history = require_monitoring_history_view.useMonitoringHistory(group ? {
618
+ clusterId: data.clusterId,
619
+ scope: group.kind === "Pod" ? "pod" : "workload",
620
+ uid: group.identity
621
+ } : void 0, live);
622
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("section", {
623
+ "aria-label": "Application resource history",
624
+ children: [
625
+ /* @__PURE__ */ require_jsx_runtime.jsxs("h3", { children: [group?.name ?? "Unavailable application", " resource history"] }),
626
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring_history_view.MonitoringHistoryRange, {
627
+ range: history.range,
628
+ onChange: history.setRange
629
+ }),
630
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", {
631
+ role: "status",
632
+ children: history.status
633
+ }),
634
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: live.length ? "Live usage includes every current pod; source metric timestamps are used" : "Complete current usage unavailable; missing coverage remains a gap" }),
635
+ ["cpu", "memory"].map((metric) => /* @__PURE__ */ require_jsx_runtime.jsx(require_monitoring.MetricChart, {
636
+ metric,
637
+ samples: history.samples,
638
+ scope: "pod",
639
+ status: live.length ? "" : "Complete live usage unavailable"
640
+ }, metric))
641
+ ]
642
+ });
643
+ }
644
+ //#endregion
645
+ exports.DiagnosticsPage = DiagnosticsPage;
646
+
647
+ //# sourceMappingURL=diagnostics.js.map