@64mb/freelens-power 1.4.0 → 1.6.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 (107) hide show
  1. package/README.md +7 -4
  2. package/out/main/ai-ipc.js +34 -3
  3. package/out/main/ai-ipc.js.map +1 -1
  4. package/out/main/common/power-logs-limits.js +227 -0
  5. package/out/main/common/power-logs-limits.js.map +1 -0
  6. package/out/main/common/power-logs.js +32 -0
  7. package/out/main/common/power-logs.js.map +1 -0
  8. package/out/main/common/resource-search.js +61 -0
  9. package/out/main/common/resource-search.js.map +1 -0
  10. package/out/main/common/store/logs-preferences-store.js +45 -0
  11. package/out/main/common/store/logs-preferences-store.js.map +1 -0
  12. package/out/main/common/store/monitoring-preferences-store.js +5 -0
  13. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  14. package/out/main/dashboard-icon-cache.js +1 -1
  15. package/out/main/index.js +6 -0
  16. package/out/main/index.js.map +1 -1
  17. package/out/main/power-logs-budget.js +126 -0
  18. package/out/main/power-logs-budget.js.map +1 -0
  19. package/out/main/power-logs-child.js +86 -0
  20. package/out/main/power-logs-child.js.map +1 -0
  21. package/out/main/power-logs-database.js +179 -0
  22. package/out/main/power-logs-database.js.map +1 -0
  23. package/out/main/power-logs-ipc.js +275 -0
  24. package/out/main/power-logs-ipc.js.map +1 -0
  25. package/out/main/power-logs-query-child.js +23 -0
  26. package/out/main/power-logs-query-child.js.map +1 -0
  27. package/out/main/power-logs-query.js +127 -0
  28. package/out/main/power-logs-query.js.map +1 -0
  29. package/out/main/power-logs-worker.js +215 -0
  30. package/out/main/power-logs-worker.js.map +1 -0
  31. package/out/main/resource-search-database.js +209 -0
  32. package/out/main/resource-search-database.js.map +1 -0
  33. package/out/main/resource-search-worker.js +56 -0
  34. package/out/main/resource-search-worker.js.map +1 -0
  35. package/out/main/resource-search.js +155 -0
  36. package/out/main/resource-search.js.map +1 -0
  37. package/out/renderer/ai-logs-sql.js +27 -0
  38. package/out/renderer/ai-logs-sql.js.map +1 -0
  39. package/out/renderer/ai-message.js +48 -27
  40. package/out/renderer/ai-message.js.map +1 -1
  41. package/out/renderer/ai-terminal-context.js +50 -40
  42. package/out/renderer/ai-terminal-context.js.map +1 -1
  43. package/out/renderer/ai.js +15 -2
  44. package/out/renderer/ai.js.map +1 -1
  45. package/out/renderer/appearance.js +2 -1
  46. package/out/renderer/appearance.js.map +1 -1
  47. package/out/renderer/gateway-relationships-client.js +107 -0
  48. package/out/renderer/gateway-relationships-client.js.map +1 -0
  49. package/out/renderer/index.js +67 -29
  50. package/out/renderer/index.js.map +1 -1
  51. package/out/renderer/power-log-json.js +29 -0
  52. package/out/renderer/power-log-json.js.map +1 -0
  53. package/out/renderer/power-logs-client.js +35 -0
  54. package/out/renderer/power-logs-client.js.map +1 -0
  55. package/out/renderer/power-logs-collector.js +316 -0
  56. package/out/renderer/power-logs-collector.js.map +1 -0
  57. package/out/renderer/power-logs-entrypoints.js +201 -0
  58. package/out/renderer/power-logs-entrypoints.js.map +1 -0
  59. package/out/renderer/power-logs-sql-row.js +52 -0
  60. package/out/renderer/power-logs-sql-row.js.map +1 -0
  61. package/out/renderer/power-logs-transport.js +122 -0
  62. package/out/renderer/power-logs-transport.js.map +1 -0
  63. package/out/renderer/power-logs.css.js +116 -0
  64. package/out/renderer/power-logs.css.js.map +1 -0
  65. package/out/renderer/power-logs.js +1352 -0
  66. package/out/renderer/power-logs.js.map +1 -0
  67. package/out/renderer/preferences/logs-preferences.js +93 -0
  68. package/out/renderer/preferences/logs-preferences.js.map +1 -0
  69. package/out/renderer/preferences/monitoring-preferences.js +10 -0
  70. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  71. package/out/renderer/preferences/preferences.js +24 -10
  72. package/out/renderer/preferences/preferences.js.map +1 -1
  73. package/out/renderer/resource-relationships.css.js +1 -1
  74. package/out/renderer/resource-relationships.css.js.map +1 -1
  75. package/out/renderer/resource-relationships.js +46 -8
  76. package/out/renderer/resource-relationships.js.map +1 -1
  77. package/out/renderer/resource-search-client.js +116 -0
  78. package/out/renderer/resource-search-client.js.map +1 -0
  79. package/out/renderer/resource-search-discovery.js +79 -0
  80. package/out/renderer/resource-search-discovery.js.map +1 -0
  81. package/out/renderer/resource-search-model.js +2 -1
  82. package/out/renderer/resource-search-model.js.map +1 -1
  83. package/out/renderer/resource-search-routing.js +78 -0
  84. package/out/renderer/resource-search-routing.js.map +1 -0
  85. package/out/renderer/resource-search-sync.js +199 -0
  86. package/out/renderer/resource-search-sync.js.map +1 -0
  87. package/out/renderer/resource-search.js +279 -54
  88. package/out/renderer/resource-search.js.map +1 -1
  89. package/out/renderer/src/common/gateway-relationships.js +122 -0
  90. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  91. package/out/renderer/src/common/power-log-parser.js +114 -0
  92. package/out/renderer/src/common/power-log-parser.js.map +1 -0
  93. package/out/renderer/src/common/power-logs-ai.js +56 -0
  94. package/out/renderer/src/common/power-logs-ai.js.map +1 -0
  95. package/out/renderer/src/common/power-logs-limits.js +227 -0
  96. package/out/renderer/src/common/power-logs-limits.js.map +1 -0
  97. package/out/renderer/src/common/power-logs.js +32 -0
  98. package/out/renderer/src/common/power-logs.js.map +1 -0
  99. package/out/renderer/src/common/resource-relationships.js +8 -0
  100. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  101. package/out/renderer/src/common/resource-search.js +6 -0
  102. package/out/renderer/src/common/resource-search.js.map +1 -0
  103. package/out/renderer/src/common/store/logs-preferences-store.js +45 -0
  104. package/out/renderer/src/common/store/logs-preferences-store.js.map +1 -0
  105. package/out/renderer/src/common/store/monitoring-preferences-store.js +5 -0
  106. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  107. package/package.json +8 -2
@@ -0,0 +1,1352 @@
1
+ const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
+ const require__global_external_mobx_react = require("./_virtual/_global-external_mobx-react.js");
3
+ const require_power_logs = require("./src/common/power-logs.js");
4
+ const require_power_logs_limits = require("./src/common/power-logs-limits.js");
5
+ const require_logs_preferences_store = require("./src/common/store/logs-preferences-store.js");
6
+ const require__global_external_react = require("./_virtual/_global-external_react.js");
7
+ const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
8
+ const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
9
+ const require_date_time = require("./src/common/date-time.js");
10
+ const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
11
+ const require_power_logs_ai = require("./src/common/power-logs-ai.js");
12
+ const require_power_log_json = require("./power-log-json.js");
13
+ const require_power_logs_css = require("./power-logs.css.js");
14
+ const require_power_logs_client = require("./power-logs-client.js");
15
+ const require_power_logs_collector = require("./power-logs-collector.js");
16
+ const require_power_logs_entrypoints = require("./power-logs-entrypoints.js");
17
+ const require_power_logs_sql_row = require("./power-logs-sql-row.js");
18
+ const require_resource_search_client = require("./resource-search-client.js");
19
+ const require_power_logs_transport = require("./power-logs-transport.js");
20
+ let node_crypto = require("node:crypto");
21
+ //#region src/renderer/power-logs.tsx
22
+ var Icon = (props) => /* @__PURE__ */ require_jsx_runtime.jsx("span", {
23
+ "aria-hidden": "true",
24
+ style: { display: "inline-flex" },
25
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, { ...props })
26
+ });
27
+ var sourceKey = (r) => `${r.pod} / ${r.container}${r.previous ? " (previous)" : ""}`;
28
+ var levels = [
29
+ "trace",
30
+ "debug",
31
+ "info",
32
+ "warn",
33
+ "error",
34
+ "fatal",
35
+ "unknown"
36
+ ];
37
+ var examples = [
38
+ ["Recent", "SELECT message, level, container, pod, timestamp FROM logs ORDER BY timestamp DESC NULLS LAST LIMIT 200"],
39
+ ["Errors", "SELECT pod, container, count(*) AS errors FROM logs WHERE level IN ('error','fatal') GROUP BY pod, container ORDER BY errors DESC"],
40
+ ["Timeline", "SELECT strftime('%Y-%m-%d %H:%M', timestamp / 1000, 'unixepoch') AS minute, level, count(*) AS lines FROM logs GROUP BY minute, level ORDER BY minute"],
41
+ ["JSON", "SELECT pod, json_extract(fieldsJson, '$.message') AS message FROM logs WHERE fieldsJson IS NOT NULL LIMIT 100"]
42
+ ];
43
+ var sqlColumnWidth = (column) => column === "message" || column === "fieldsJson" ? 360 : column === "level" ? 90 : 200;
44
+ function initialTarget() {
45
+ try {
46
+ return require_power_logs_entrypoints.parsePowerLogsTarget(JSON.parse(new URLSearchParams(window.location.search).get("target") ?? "null"));
47
+ } catch {
48
+ return;
49
+ }
50
+ }
51
+ function initialSource(container = new URLSearchParams(window.location.search).get("container")) {
52
+ return typeof container === "string" && /^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/.test(container) && container.length <= 63 ? `container:${container}` : "";
53
+ }
54
+ function LogSelect({ label, value, options, onChange, disabled = false, renderOption, onKeyDown }) {
55
+ return /* @__PURE__ */ require_jsx_runtime.jsx("span", {
56
+ className: "power-logs-select",
57
+ "data-source-select": label === "Container source" || void 0,
58
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Select, {
59
+ "aria-label": label,
60
+ value,
61
+ options,
62
+ formatOptionLabel: (option, meta) => renderOption ? renderOption(option, meta.context) : label === "Log level" && option.value ? /* @__PURE__ */ require_jsx_runtime.jsx("span", {
63
+ className: "power-logs-level-label",
64
+ "data-level": option.value,
65
+ children: option.label
66
+ }) : option.label,
67
+ isDisabled: disabled,
68
+ onKeyDown,
69
+ tabSelectsValue: false,
70
+ isClearable: false,
71
+ menuPosition: "fixed",
72
+ menuPortalTarget: document.body,
73
+ styles: {
74
+ menu: (base) => label === "Container source" ? {
75
+ ...base,
76
+ width: "100%",
77
+ maxWidth: "100%"
78
+ } : base,
79
+ menuList: (base) => label === "Container source" ? {
80
+ ...base,
81
+ width: "100%",
82
+ maxWidth: "100%"
83
+ } : base,
84
+ option: (base) => label === "Container source" ? {
85
+ ...base,
86
+ whiteSpace: "normal"
87
+ } : base,
88
+ menuPortal: (base) => {
89
+ if (label !== "Container source" && label !== "Capture snapshot") return {
90
+ ...base,
91
+ zIndex: 1e4
92
+ };
93
+ const width = Math.min(label === "Capture snapshot" ? 620 : 560, window.innerWidth - 32);
94
+ return {
95
+ ...base,
96
+ zIndex: 1e4,
97
+ width,
98
+ left: Math.max(16, Math.min(typeof base.left === "number" ? base.left : 16, window.innerWidth - width - 16))
99
+ };
100
+ }
101
+ },
102
+ onChange: (option) => {
103
+ if (option) onChange(option.value);
104
+ }
105
+ })
106
+ });
107
+ }
108
+ var SourcePicker = require__global_external_mobx_react.observer(() => {
109
+ const targets = [
110
+ require_extensions.Renderer.K8sApi.podsStore,
111
+ require_extensions.Renderer.K8sApi.deploymentStore,
112
+ require_extensions.Renderer.K8sApi.daemonSetStore,
113
+ require_extensions.Renderer.K8sApi.statefulSetStore,
114
+ require_extensions.Renderer.K8sApi.replicaSetStore,
115
+ require_extensions.Renderer.K8sApi.jobStore,
116
+ require_extensions.Renderer.K8sApi.cronJobStore
117
+ ].flatMap((store) => store.getItems().flatMap((object) => {
118
+ const target = require_power_logs_entrypoints.parsePowerLogsTarget({
119
+ kind: object.kind,
120
+ ...object.metadata
121
+ });
122
+ return target ? [target] : [];
123
+ }));
124
+ const [value, setValue] = require__global_external_react.useState("");
125
+ const sorted = [...targets].sort((a, b) => `${a.namespace}/${a.kind}/${a.name}`.localeCompare(`${b.namespace}/${b.kind}/${b.name}`));
126
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
127
+ className: "power-logs-source",
128
+ children: [
129
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: "Resource" }),
130
+ /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
131
+ label: "Resource",
132
+ value,
133
+ onChange: setValue,
134
+ options: [{
135
+ value: "",
136
+ label: "Choose a loaded pod or workload"
137
+ }, ...sorted.map((target) => ({
138
+ value: target.uid,
139
+ label: `${target.namespace} / ${target.kind} / ${target.name}`
140
+ }))]
141
+ }),
142
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
143
+ type: "button",
144
+ disabled: !targets.some((target) => target.uid === value),
145
+ onClick: () => {
146
+ const target = targets.find((t) => t.uid === value);
147
+ if (target) require_power_logs_entrypoints.openPowerLogs(target);
148
+ },
149
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
150
+ material: "subject",
151
+ size: 16
152
+ }), " Open logs"]
153
+ })
154
+ ]
155
+ });
156
+ });
157
+ function LogRows({ records, selected, onSelect, follow, setFollow, compact }) {
158
+ const viewport = require__global_external_react.useRef(null);
159
+ const [top, setTop] = require__global_external_react.useState(0);
160
+ const [height, setHeight] = require__global_external_react.useState(400);
161
+ require__global_external_react.useEffect(() => {
162
+ const element = viewport.current;
163
+ if (!element) return;
164
+ const resize = new ResizeObserver(() => setHeight(element.clientHeight));
165
+ resize.observe(element);
166
+ return () => resize.disconnect();
167
+ }, []);
168
+ require__global_external_react.useLayoutEffect(() => {
169
+ if (follow && viewport.current) {
170
+ viewport.current.scrollTop = viewport.current.scrollHeight;
171
+ setTop(viewport.current.scrollTop);
172
+ }
173
+ }, [
174
+ records,
175
+ follow,
176
+ compact
177
+ ]);
178
+ const rowHeight = compact ? 24 : 34;
179
+ const start = Math.max(0, Math.floor(top / rowHeight) - 5);
180
+ const visible = records.slice(start, start + Math.ceil(height / rowHeight) + 10);
181
+ return /* @__PURE__ */ require_jsx_runtime.jsx("div", {
182
+ className: "power-logs-viewer",
183
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("div", {
184
+ className: "power-logs-stream",
185
+ ref: viewport,
186
+ "aria-label": "Log lines",
187
+ onScroll: (event) => {
188
+ const el = event.currentTarget;
189
+ setTop(el.scrollTop);
190
+ if (el.scrollHeight - el.scrollTop - el.clientHeight > 60) setFollow(false);
191
+ },
192
+ children: records.length === 0 ? /* @__PURE__ */ require_jsx_runtime.jsx("div", {
193
+ className: "power-logs-empty",
194
+ children: "No matching log lines"
195
+ }) : /* @__PURE__ */ require_jsx_runtime.jsx("div", {
196
+ style: {
197
+ height: records.length * rowHeight,
198
+ position: "relative"
199
+ },
200
+ children: visible.map((record, i) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
201
+ type: "button",
202
+ className: "power-log-row",
203
+ "data-level": record.level,
204
+ "aria-pressed": selected === record.key,
205
+ style: {
206
+ top: (start + i) * rowHeight,
207
+ height: rowHeight
208
+ },
209
+ onClick: () => onSelect(record),
210
+ children: [
211
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
212
+ title: record.timestamp !== null ? require_date_time.formatDateTime(record.timestamp) : "Timestamp unavailable",
213
+ children: record.timestamp !== null ? require_date_time.formatDateTime(record.timestamp).slice(-8) : "—"
214
+ }),
215
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
216
+ className: "power-log-level",
217
+ children: record.level
218
+ }),
219
+ /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
220
+ className: "power-log-origin",
221
+ title: sourceKey(record),
222
+ children: [
223
+ record.container,
224
+ record.previous ? " (previous)" : "",
225
+ " · ",
226
+ record.pod
227
+ ]
228
+ }),
229
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: record.message })
230
+ ]
231
+ }, record.key))
232
+ })
233
+ })
234
+ });
235
+ }
236
+ function LogDetails({ record, jsonColumn, onClose }) {
237
+ const structuredFields = require__global_external_react.useMemo(() => {
238
+ if (!record?.fieldsJson) return "";
239
+ try {
240
+ return JSON.stringify(JSON.parse(record.fieldsJson), null, 2);
241
+ } catch {
242
+ return record.fieldsJson;
243
+ }
244
+ }, [record?.fieldsJson]);
245
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("aside", {
246
+ className: "power-logs-detail",
247
+ "aria-label": "Log line details",
248
+ children: [
249
+ /* @__PURE__ */ require_jsx_runtime.jsxs("header", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: "Line details" }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
250
+ className: "power-logs-detail-close",
251
+ "aria-label": "Close line details",
252
+ title: "Close line details",
253
+ onClick: onClose,
254
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
255
+ material: "close",
256
+ size: 16
257
+ })
258
+ })] }),
259
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", {
260
+ className: "power-logs-muted",
261
+ children: [record.pod && sourceKey(record), record.level].filter(Boolean).join(" · ")
262
+ }),
263
+ record.message && /* @__PURE__ */ require_jsx_runtime.jsx("pre", { children: record.message }),
264
+ record.fieldsJson && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: jsonColumn ? `Structured fields · ${jsonColumn}` : "Structured fields" }), /* @__PURE__ */ require_jsx_runtime.jsx(require_power_log_json.LogJson, { text: structuredFields })] })
265
+ ]
266
+ });
267
+ }
268
+ function Workspace({ clusterId, contextId }) {
269
+ const client = require__global_external_react.useMemo(() => require_power_logs_client.createPowerLogsClient({
270
+ clusterId,
271
+ contextId
272
+ }), [clusterId, contextId]);
273
+ const [target, setTarget] = require__global_external_react.useState(initialTarget);
274
+ const [records, setRecords] = require__global_external_react.useState([]);
275
+ const [sources, setSources] = require__global_external_react.useState([]);
276
+ const [notes, setNotes] = require__global_external_react.useState([]);
277
+ const [status, setStatus] = require__global_external_react.useState("Choose a resource to read its logs");
278
+ const [error, setError] = require__global_external_react.useState("");
279
+ const [tab, setTab] = require__global_external_react.useState("live");
280
+ const [text, setText] = require__global_external_react.useState("");
281
+ const [level, setLevel] = require__global_external_react.useState("");
282
+ const [source, setSource] = require__global_external_react.useState(initialSource);
283
+ const preferences = require_logs_preferences_store.LogsPreferencesStore.getInstanceOrCreate();
284
+ const limits = require_power_logs_limits.normalizePowerLogsLimits(preferences.limits);
285
+ require__global_external_react.useEffect(() => {
286
+ require_power_logs_limits.applyPowerLogsLimits(preferences.limits);
287
+ }, [preferences.limits]);
288
+ const [compact, setCompact] = require__global_external_react.useState(preferences.compactRows === true);
289
+ const [selected, setSelected] = require__global_external_react.useState();
290
+ const [selectedSqlRow, setSelectedSqlRow] = require__global_external_react.useState();
291
+ const [liveCoverage, setLiveCoverage] = require__global_external_react.useState();
292
+ const [live, setLive] = require__global_external_react.useState(false);
293
+ const [follow, setFollow] = require__global_external_react.useState(true);
294
+ const [busy, setBusy] = require__global_external_react.useState(false);
295
+ const [captures, setCaptures] = require__global_external_react.useState([]);
296
+ const [captureId, setCaptureId] = require__global_external_react.useState("");
297
+ const captureSelection = require__global_external_react.useRef(captureId);
298
+ captureSelection.current = captureId;
299
+ const deleting = require__global_external_react.useRef(/* @__PURE__ */ new Set());
300
+ const [deletingCount, setDeletingCount] = require__global_external_react.useState(0);
301
+ const [capturing, setCapturing] = require__global_external_react.useState();
302
+ const [capturePending, setCapturePending] = require__global_external_react.useState(false);
303
+ const [captureMinutes, setCaptureMinutes] = require__global_external_react.useState(15);
304
+ const [previous, setPrevious] = require__global_external_react.useState(false);
305
+ const [sql, setSql] = require__global_external_react.useState(examples[0][1]);
306
+ const [result, setResult] = require__global_external_react.useState();
307
+ const [resultEvidence, setResultEvidence] = require__global_external_react.useState();
308
+ const [resultPage, setResultPage] = require__global_external_react.useState(0);
309
+ const [queryId, setQueryId] = require__global_external_react.useState("");
310
+ const captureOptions = require__global_external_react.useRef(null);
311
+ require__global_external_react.useEffect(() => {
312
+ const outside = (event) => {
313
+ if (!captureOptions.current?.contains(event.target) && !event.target?.closest?.(".Select__menu")) {
314
+ if (captureOptions.current) captureOptions.current.open = false;
315
+ }
316
+ };
317
+ const escape = (event) => {
318
+ if (event.key !== "Escape" || event.defaultPrevented || event.target?.closest?.(".Select")) return;
319
+ if (captureOptions.current?.open) captureOptions.current.open = false;
320
+ else {
321
+ setSelected(void 0);
322
+ setSelectedSqlRow(void 0);
323
+ }
324
+ };
325
+ window.addEventListener("pointerdown", outside);
326
+ window.addEventListener("keydown", escape);
327
+ return () => {
328
+ window.removeEventListener("pointerdown", outside);
329
+ window.removeEventListener("keydown", escape);
330
+ };
331
+ }, []);
332
+ const historyController = require__global_external_react.useRef();
333
+ const liveController = require__global_external_react.useRef();
334
+ const captureController = require__global_external_react.useRef();
335
+ const queryRef = require__global_external_react.useRef("");
336
+ const epoch = require__global_external_react.useRef(0);
337
+ const alive = require__global_external_react.useRef(true);
338
+ const sequence = require__global_external_react.useRef(0);
339
+ const recordsRef = require__global_external_react.useRef(records);
340
+ recordsRef.current = records;
341
+ const pending = require__global_external_react.useRef([]);
342
+ const flushTimer = require__global_external_react.useRef();
343
+ const refreshCaptures = () => client.list().then((value) => {
344
+ if (alive.current) setCaptures(value);
345
+ }).catch(() => {
346
+ if (alive.current) setError("Temporary captures are unavailable");
347
+ });
348
+ const stopLive = () => {
349
+ liveController.current?.abort();
350
+ liveController.current = void 0;
351
+ setLive(false);
352
+ };
353
+ const flush = () => {
354
+ clearTimeout(flushTimer.current);
355
+ flushTimer.current = void 0;
356
+ const batch = pending.current;
357
+ pending.current = [];
358
+ if (!batch.length || !alive.current) return;
359
+ const next = batch.map((record) => ({
360
+ ...record,
361
+ key: ++sequence.current,
362
+ bytes: Buffer.byteLength(JSON.stringify(record))
363
+ }));
364
+ setRecords((current) => {
365
+ const combined = [...current, ...next];
366
+ let bytes = combined.reduce((sum, r) => sum + r.bytes, 0);
367
+ let start = 0;
368
+ while (combined.length - start > require_power_logs.POWER_LOGS_LIMITS.liveRows || bytes > require_power_logs.POWER_LOGS_LIMITS.liveBytes) bytes -= combined[start++].bytes;
369
+ return combined.slice(start).sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0) || a.key - b.key);
370
+ });
371
+ };
372
+ const appendView = (batch) => {
373
+ if (!alive.current) return;
374
+ pending.current.push(...batch);
375
+ if (pending.current.length > 1e3) flush();
376
+ else flushTimer.current ??= setTimeout(flush, 80);
377
+ };
378
+ require__global_external_react.useEffect(() => {
379
+ alive.current = true;
380
+ const onTarget = (event) => {
381
+ const next = require_power_logs_entrypoints.parsePowerLogsTarget(event.detail);
382
+ if (next) {
383
+ if (queryRef.current) {
384
+ client.cancel(queryRef.current).catch(() => {});
385
+ queryRef.current = "";
386
+ setQueryId("");
387
+ }
388
+ setSource(initialSource(event.detail.container));
389
+ setTarget(next);
390
+ }
391
+ };
392
+ window.addEventListener("power-logs-target", onTarget);
393
+ refreshCaptures();
394
+ const timer = setInterval(() => void refreshCaptures(), 3e4);
395
+ return () => {
396
+ alive.current = false;
397
+ epoch.current++;
398
+ clearInterval(timer);
399
+ clearTimeout(flushTimer.current);
400
+ historyController.current?.abort();
401
+ liveController.current?.abort();
402
+ captureController.current?.abort();
403
+ if (queryRef.current) client.cancel(queryRef.current).catch(() => {});
404
+ window.removeEventListener("power-logs-target", onTarget);
405
+ };
406
+ }, [client]);
407
+ const readHistory = async () => {
408
+ if (!target) return;
409
+ const currentEpoch = ++epoch.current;
410
+ historyController.current?.abort();
411
+ stopLive();
412
+ const controller = new AbortController();
413
+ historyController.current = controller;
414
+ clearTimeout(flushTimer.current);
415
+ flushTimer.current = void 0;
416
+ pending.current = [];
417
+ setBusy(true);
418
+ setRecords([]);
419
+ setSelected(void 0);
420
+ setError("");
421
+ setNotes([]);
422
+ setStatus("Discovering owned containers…");
423
+ try {
424
+ const transport = require_power_logs_transport.createPowerLogsTransport(clusterId);
425
+ const discovery = await require_power_logs_collector.resolveLogSources(target, transport, controller.signal);
426
+ if (controller.signal.aborted || currentEpoch !== epoch.current) return;
427
+ setSources(discovery.sources);
428
+ setNotes(discovery.notes);
429
+ setStatus(`Reading ${discovery.sources.length} container sources…`);
430
+ const history = await require_power_logs_collector.collectHistoricalLogs(discovery.sources, transport, {
431
+ signal: controller.signal,
432
+ tailLines: require_power_logs.POWER_LOGS_LIMITS.historyLines,
433
+ limitBytes: require_power_logs.POWER_LOGS_LIMITS.historyBytes,
434
+ onRecords: (batch) => {
435
+ if (currentEpoch === epoch.current) appendView(batch);
436
+ }
437
+ });
438
+ if (controller.signal.aborted || currentEpoch !== epoch.current) return;
439
+ flush();
440
+ setNotes([.../* @__PURE__ */ new Set([...discovery.notes, ...history.notes])]);
441
+ setStatus(`${discovery.sources.length} sources · last ${limits.historyLines.toLocaleString()} lines per container, up to ${limits.historyBytes / 1048576} MiB each`);
442
+ } catch {
443
+ if (alive.current && currentEpoch === epoch.current) setError(controller.signal.aborted ? "Log loading stopped" : "Could not read this resource's logs. Check permissions and whether it still exists.");
444
+ } finally {
445
+ if (alive.current && currentEpoch === epoch.current) setBusy(false);
446
+ }
447
+ };
448
+ require__global_external_react.useEffect(() => {
449
+ setText("");
450
+ setSources([]);
451
+ setTab("live");
452
+ readHistory();
453
+ }, [target?.uid]);
454
+ const toggleLive = () => {
455
+ if (live) {
456
+ stopLive();
457
+ return;
458
+ }
459
+ setLive(true);
460
+ };
461
+ require__global_external_react.useEffect(() => {
462
+ if (!live || !sources.length) return;
463
+ const controller = new AbortController();
464
+ liveController.current = controller;
465
+ const collector = require_power_logs_collector.startLiveLogs(sources, require_power_logs_transport.createPowerLogsTransport(clusterId), {
466
+ signal: controller.signal,
467
+ tailLines: require_power_logs.POWER_LOGS_LIMITS.historyLines,
468
+ initialRecords: recordsRef.current,
469
+ onRecords: appendView,
470
+ onCoverage: (coverage) => {
471
+ if (alive.current && liveController.current === controller) setLiveCoverage({
472
+ ...coverage,
473
+ notes: [...coverage.notes]
474
+ });
475
+ }
476
+ });
477
+ setLiveCoverage({ ...collector.coverage });
478
+ collector.done.finally(() => {
479
+ if (alive.current && !controller.signal.aborted && liveController.current === controller) {
480
+ liveController.current = void 0;
481
+ setLive(false);
482
+ }
483
+ });
484
+ return () => {
485
+ controller.abort();
486
+ if (liveController.current === controller) liveController.current = void 0;
487
+ };
488
+ }, [live, sources.map((item) => `${item.podUid}/${item.container}`).join(";")]);
489
+ require__global_external_react.useEffect(() => {
490
+ if (!live || !target) return;
491
+ const controller = new AbortController();
492
+ const startingEpoch = epoch.current;
493
+ let refreshing = false;
494
+ const timer = setInterval(() => {
495
+ if (refreshing) return;
496
+ refreshing = true;
497
+ require_power_logs_collector.resolveLogSources(target, require_power_logs_transport.createPowerLogsTransport(clusterId), controller.signal).then((discovery) => {
498
+ if (controller.signal.aborted || epoch.current !== startingEpoch) return;
499
+ setSources(discovery.sources);
500
+ setNotes(discovery.notes);
501
+ setStatus(`${discovery.sources.length} sources · ownership inventory refreshed`);
502
+ }).catch(() => {
503
+ if (!controller.signal.aborted && alive.current && epoch.current === startingEpoch) {
504
+ stopLive();
505
+ setNotes((current) => [.../* @__PURE__ */ new Set([...current, "Workload inventory changed or became unavailable. Refresh before following logs again."])]);
506
+ }
507
+ }).finally(() => {
508
+ refreshing = false;
509
+ });
510
+ }, 3e4);
511
+ return () => {
512
+ clearInterval(timer);
513
+ controller.abort();
514
+ };
515
+ }, [live, target?.uid]);
516
+ const capture = async () => {
517
+ if (!target || captureController.current || queryRef.current) return;
518
+ setCapturePending(true);
519
+ const controller = new AbortController();
520
+ captureController.current = controller;
521
+ let current;
522
+ let partial = false;
523
+ let captureNotes = [];
524
+ setError("");
525
+ stopLive();
526
+ try {
527
+ current = await client.create(target);
528
+ setCapturing(current);
529
+ const transport = require_power_logs_transport.createPowerLogsTransport(clusterId);
530
+ const discovery = await require_power_logs_collector.resolveLogSources(target, transport, controller.signal);
531
+ captureNotes = discovery.notes;
532
+ partial = discovery.partial;
533
+ const captureSources = previous ? [...discovery.sources, ...discovery.sources.map((item) => ({
534
+ ...item,
535
+ previous: true
536
+ }))] : discovery.sources;
537
+ const boundary = current.createdAt;
538
+ const history = await require_power_logs_collector.collectHistoricalLogs(captureSources, transport, {
539
+ signal: controller.signal,
540
+ tailLines: require_power_logs.POWER_LOGS_LIMITS.captureSourceLines,
541
+ sinceTime: (/* @__PURE__ */ new Date(boundary - captureMinutes * 6e4)).toISOString(),
542
+ limitBytes: require_power_logs.POWER_LOGS_LIMITS.captureSourceBytes,
543
+ onRecords: async (batch) => {
544
+ if (batch.some((record) => record.timestamp === null)) {
545
+ partial = true;
546
+ captureNotes.push("Some records have no timestamp and their capture window cannot be verified");
547
+ }
548
+ batch = batch.filter((record) => record.timestamp === null || record.timestamp <= boundary && record.timestamp >= boundary - captureMinutes * 6e4);
549
+ for (let offset = 0; offset < batch.length;) {
550
+ const chunk = [];
551
+ let bytes = 2;
552
+ while (offset < batch.length && chunk.length < require_power_logs.POWER_LOGS_LIMITS.batchRows) {
553
+ const record = batch[offset];
554
+ const size = Buffer.byteLength(JSON.stringify(record)) + 1;
555
+ if (chunk.length && bytes + size > require_power_logs.POWER_LOGS_LIMITS.batchBytes) break;
556
+ chunk.push(record);
557
+ bytes += size;
558
+ offset++;
559
+ }
560
+ if (controller.signal.aborted) throw new Error("Capture stopped");
561
+ try {
562
+ current = await client.append(current.id, chunk);
563
+ if (alive.current) setCapturing(current);
564
+ } catch {
565
+ controller.abort();
566
+ throw new Error("Capture reached its storage limit or became unavailable");
567
+ }
568
+ }
569
+ }
570
+ });
571
+ partial ||= history.partial;
572
+ captureNotes.push(...history.notes);
573
+ } catch {
574
+ partial = true;
575
+ captureNotes.push("Collection stopped or a source was unavailable; this snapshot may be incomplete");
576
+ } finally {
577
+ if (current) try {
578
+ const saved = await client.finish(current.id, partial || controller.signal.aborted, [...new Set(captureNotes)].slice(0, 20));
579
+ if (alive.current) {
580
+ setCaptureId(saved.id);
581
+ setCaptures((items) => [saved, ...items.filter((item) => item.id !== saved.id)]);
582
+ setTab("captures");
583
+ setSql(examples[0][1]);
584
+ runQuery(saved, examples[0][1]);
585
+ }
586
+ } catch {
587
+ if (alive.current) setError("The temporary capture could not be finalized");
588
+ }
589
+ else if (alive.current) setError("Could not create a capture. Delete an old snapshot or check the log service.");
590
+ if (alive.current) {
591
+ setCapturing(void 0);
592
+ setCapturePending(false);
593
+ refreshCaptures();
594
+ }
595
+ captureController.current = void 0;
596
+ }
597
+ };
598
+ const runQuery = async (selectedCapture, query = sql) => {
599
+ if (queryRef.current || !selectedCapture && captureController.current) return;
600
+ const captured = selectedCapture ?? captures.find((item) => item.id === captureId);
601
+ if (!captured || captured.state === "collecting" || deleting.current.has(captured.id)) return;
602
+ const id = (0, node_crypto.randomUUID)();
603
+ queryRef.current = id;
604
+ setQueryId(id);
605
+ setError("");
606
+ setResult(void 0);
607
+ setSelectedSqlRow(void 0);
608
+ setResultEvidence({
609
+ capture: captured,
610
+ sql: query
611
+ });
612
+ setResultPage(0);
613
+ try {
614
+ const value = await client.query(captured.id, id, query);
615
+ if (alive.current && queryRef.current === id) if (value.error) setError(value.error);
616
+ else setResult(value);
617
+ } catch {
618
+ if (alive.current && queryRef.current === id) setError("Query stopped or failed. Use one read-only SELECT or EXPLAIN; external files, networking and extensions are disabled.");
619
+ } finally {
620
+ if (queryRef.current === id) {
621
+ if (alive.current) setQueryId("");
622
+ queryRef.current = "";
623
+ }
624
+ }
625
+ };
626
+ const filtered = require__global_external_react.useMemo(() => records.filter((record) => (!text || record.message.toLocaleLowerCase().includes(text.toLocaleLowerCase())) && (!level || record.level === level) && (!source || (source.startsWith("container:") ? record.container === source.slice(10) : sourceKey(record) === source))), [
627
+ records,
628
+ text,
629
+ level,
630
+ source
631
+ ]);
632
+ const chosenCapture = captures.find((item) => item.id === captureId);
633
+ const collectionNotes = tab === "live" ? [.../* @__PURE__ */ new Set([...notes, ...live ? liveCoverage?.notes ?? [] : []])] : [];
634
+ require__global_external_react.useEffect(() => {
635
+ window.dispatchEvent(new CustomEvent("power-logs-workspace-change", { detail: { active: true } }));
636
+ return () => {
637
+ window.dispatchEvent(new CustomEvent("power-logs-workspace-change", { detail: { active: false } }));
638
+ };
639
+ }, []);
640
+ require__global_external_react.useEffect(() => {
641
+ const insert = (event) => {
642
+ const sql = event.detail?.sql;
643
+ if (typeof sql !== "string" || !sql.trim() || sql.length > 65536) return;
644
+ setTab("captures");
645
+ setSql(sql);
646
+ setSelectedSqlRow(void 0);
647
+ if (!captureId) {
648
+ const latest = captures.find((item) => item.state !== "collecting" && item.target.uid === target?.uid);
649
+ if (latest) setCaptureId(latest.id);
650
+ }
651
+ requestAnimationFrame(() => document.getElementById("power-logs-sql")?.focus());
652
+ };
653
+ window.addEventListener("power-logs-sql-draft", insert);
654
+ return () => window.removeEventListener("power-logs-sql-draft", insert);
655
+ }, [
656
+ captureId,
657
+ captures,
658
+ target?.uid
659
+ ]);
660
+ const attachAi = () => {
661
+ const resource = tab === "captures" ? result && resultEvidence ? resultEvidence.capture.target : chosenCapture?.target ?? target : target;
662
+ const summary = tab === "captures" && result && resultEvidence ? require_power_logs_ai.powerLogQueryEvidence(resultEvidence.capture, resultEvidence.sql, selectedSqlRow ? {
663
+ ...result,
664
+ rows: [selectedSqlRow.row],
665
+ truncated: false
666
+ } : result) : tab === "live" && selected ? require_power_logs_ai.powerLogLineEvidence(resource, selected) : JSON.stringify({
667
+ target: resource,
668
+ ...tab === "captures" ? {
669
+ sql,
670
+ rows: [],
671
+ state: "Draft query, not executed"
672
+ } : {
673
+ status,
674
+ sources: sources.length
675
+ }
676
+ });
677
+ if (!require_ai_diagnostic_handoff.explainDiagnosticFinding({
678
+ clusterId,
679
+ title: tab === "captures" ? selectedSqlRow ? "SQL row" : "SQL query" : selected ? "Log line" : "Power Logs",
680
+ summary: summary.slice(0, 6e3),
681
+ ...resource ? { resource: {
682
+ kind: resource.kind,
683
+ name: resource.name,
684
+ namespace: resource.namespace
685
+ } } : {}
686
+ })) window.dispatchEvent(new Event("power-ai-open-only"));
687
+ };
688
+ const deleteCapture = async (id) => {
689
+ if (deleting.current.has(id) || queryRef.current || captureController.current) return;
690
+ deleting.current.add(id);
691
+ setDeletingCount(deleting.current.size);
692
+ try {
693
+ await client.delete(id);
694
+ if (id === captureSelection.current) {
695
+ setCaptureId("");
696
+ setResult(void 0);
697
+ setResultEvidence(void 0);
698
+ setSelectedSqlRow(void 0);
699
+ }
700
+ await refreshCaptures();
701
+ } catch {
702
+ setError("Could not delete capture");
703
+ } finally {
704
+ deleting.current.delete(id);
705
+ if (alive.current) setDeletingCount(deleting.current.size);
706
+ }
707
+ };
708
+ const native = () => {
709
+ const first = sources[0];
710
+ const pod = require_extensions.Renderer.K8sApi.podsStore.getItems().find((item) => target?.kind === "Pod" ? item.metadata.uid === target.uid : item.metadata.uid === first?.podUid);
711
+ const requested = source.startsWith("container:") ? source.slice(10) : sources.find((item) => sourceKey(item) === source)?.container;
712
+ const defaultContainer = pod?.metadata.annotations?.["kubectl.kubernetes.io/default-container"];
713
+ const container = pod?.getAllContainers().find((item) => item.name === (requested ?? defaultContainer)) ?? pod?.getAllContainers()[0];
714
+ if (pod && container) require_extensions.Renderer.Component.logTabStore.createPodTab({
715
+ selectedPod: pod,
716
+ selectedContainer: container
717
+ });
718
+ else setError("Open the Pod in the native resource view to use its Logs action");
719
+ };
720
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
721
+ className: "power-logs",
722
+ "data-testid": "power-logs-page",
723
+ "data-compact": compact || void 0,
724
+ children: [
725
+ /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: require_power_logs_css.powerLogsStyles }),
726
+ /* @__PURE__ */ require_jsx_runtime.jsxs("header", { children: [
727
+ /* @__PURE__ */ require_jsx_runtime.jsx("h1", {
728
+ className: "power-logs-page-title",
729
+ children: "Power Logs"
730
+ }),
731
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
732
+ className: "power-logs-tabs",
733
+ role: "tablist",
734
+ "aria-label": "Log workspace",
735
+ onKeyDown: (event) => {
736
+ if (![
737
+ "ArrowLeft",
738
+ "ArrowRight",
739
+ "Home",
740
+ "End"
741
+ ].includes(event.key)) return;
742
+ const tabs = [...event.currentTarget.querySelectorAll("[role=tab]")];
743
+ const index = tabs.indexOf(document.activeElement);
744
+ if (index < 0) return;
745
+ event.preventDefault();
746
+ const next = event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : (index + (event.key === "ArrowRight" ? 1 : -1) + tabs.length) % tabs.length;
747
+ tabs[next]?.focus();
748
+ tabs[next]?.click();
749
+ },
750
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("button", {
751
+ role: "tab",
752
+ "aria-selected": tab === "live",
753
+ onClick: () => setTab("live"),
754
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
755
+ material: "subject",
756
+ size: 16
757
+ }), " Live view"]
758
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
759
+ role: "tab",
760
+ "aria-selected": tab === "captures",
761
+ onClick: () => {
762
+ setTab("captures");
763
+ const latest = captures.find((item) => item.state !== "collecting" && (!target || item.target.uid === target.uid));
764
+ if (!captureId && latest && !capturePending) {
765
+ setCaptureId(latest.id);
766
+ setSql(examples[0][1]);
767
+ runQuery(latest, examples[0][1]);
768
+ }
769
+ refreshCaptures();
770
+ },
771
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
772
+ material: "code",
773
+ size: 16
774
+ }), " SQL"]
775
+ })]
776
+ }),
777
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
778
+ className: "power-logs-actions",
779
+ children: [
780
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
781
+ className: "power-logs-toolbar power-logs-capture",
782
+ children: [
783
+ /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
784
+ ref: captureOptions,
785
+ className: "power-logs-options",
786
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("summary", {
787
+ title: `Capture window: ${captureMinutes} minutes`,
788
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
789
+ material: "tune",
790
+ size: 16
791
+ }), " Options"]
792
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", { children: [
793
+ " ",
794
+ /* @__PURE__ */ require_jsx_runtime.jsxs("label", { children: ["Capture window", /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
795
+ label: "Capture window",
796
+ value: String(captureMinutes),
797
+ onChange: (value) => setCaptureMinutes(Number(value)),
798
+ options: [
799
+ {
800
+ value: "5",
801
+ label: "5 minutes"
802
+ },
803
+ {
804
+ value: "15",
805
+ label: "15 minutes"
806
+ },
807
+ {
808
+ value: "60",
809
+ label: "1 hour"
810
+ }
811
+ ]
812
+ })] }),
813
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
814
+ checked: previous,
815
+ onChange: setPrevious,
816
+ children: "Include previous containers"
817
+ }),
818
+ /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Switch, {
819
+ checked: compact,
820
+ onChange: (value) => {
821
+ setCompact(value);
822
+ preferences.compactRows = value;
823
+ },
824
+ children: "Compact density"
825
+ })
826
+ ] })]
827
+ }),
828
+ tab === "captures" && /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
829
+ type: "button",
830
+ disabled: !target || capturePending || busy || !!queryId || deletingCount > 0,
831
+ onClick: () => void capture(),
832
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
833
+ material: "save_alt",
834
+ size: 16
835
+ }), " Capture logs"]
836
+ }),
837
+ capturePending && /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [/* @__PURE__ */ require_jsx_runtime.jsx("span", {
838
+ role: "status",
839
+ className: "power-logs-muted",
840
+ children: capturing ? `${capturing.rows.toLocaleString()} lines · ${(capturing.bytes / 1048576).toFixed(1)} MiB` : "Preparing capture…"
841
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
842
+ type: "button",
843
+ onClick: () => captureController.current?.abort(),
844
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
845
+ material: "stop",
846
+ size: 16
847
+ }), " Stop & keep partial"]
848
+ })] })
849
+ ]
850
+ }),
851
+ /* @__PURE__ */ require_jsx_runtime.jsxs(require_diagnostic_explain_button.DiagnosticExplainButton, {
852
+ "aria-label": "Open AI assistant",
853
+ title: "Open AI assistant",
854
+ onClick: attachAi,
855
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
856
+ material: "chat_bubble_outline",
857
+ size: 16
858
+ }), " AI"]
859
+ }),
860
+ tab === "live" && /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
861
+ type: "button",
862
+ disabled: !target || busy,
863
+ onClick: () => void readHistory(),
864
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
865
+ material: "refresh",
866
+ size: 16
867
+ }), " Refresh"]
868
+ }),
869
+ target?.kind === "Pod" && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
870
+ type: "button",
871
+ "aria-label": "Native logs",
872
+ title: "Native logs",
873
+ onClick: native,
874
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
875
+ material: "open_in_new",
876
+ size: 16,
877
+ tooltip: "Native logs",
878
+ tooltipOverrideDisabled: true
879
+ })
880
+ })
881
+ ]
882
+ })
883
+ ] }),
884
+ target ? /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
885
+ className: "power-logs-target power-logs-muted",
886
+ title: `${target.kind} / ${target.name} · ${target.namespace} · UID ${target.uid}`,
887
+ children: [
888
+ /* @__PURE__ */ require_jsx_runtime.jsxs("strong", { children: [
889
+ target.kind,
890
+ " / ",
891
+ target.name
892
+ ] }),
893
+ " ",
894
+ "· ",
895
+ target.namespace
896
+ ]
897
+ }) : /* @__PURE__ */ require_jsx_runtime.jsx(SourcePicker, {}),
898
+ error && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
899
+ role: "alert",
900
+ className: "power-logs-notice power-logs-error",
901
+ children: error
902
+ }),
903
+ tab === "live" ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
904
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
905
+ className: "power-logs-filters",
906
+ children: [
907
+ /* @__PURE__ */ require_jsx_runtime.jsx("input", {
908
+ "aria-label": "Search log messages",
909
+ placeholder: "Search messages…",
910
+ value: text,
911
+ maxLength: 300,
912
+ onChange: (event) => setText(event.target.value)
913
+ }),
914
+ /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
915
+ label: "Log level",
916
+ value: level,
917
+ onChange: setLevel,
918
+ options: [{
919
+ value: "",
920
+ label: "All levels"
921
+ }, ...levels.map((value) => ({
922
+ value,
923
+ label: value.toUpperCase()
924
+ }))]
925
+ }),
926
+ /* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
927
+ label: "Container source",
928
+ renderOption: (option, context) => {
929
+ if (context !== "menu") return /* @__PURE__ */ require_jsx_runtime.jsx("span", {
930
+ title: option.label,
931
+ children: option.label
932
+ });
933
+ const item = sources.find((item) => sourceKey(item) === option.value);
934
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
935
+ className: "power-logs-source-option",
936
+ title: option.label,
937
+ children: [
938
+ /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
939
+ material: item ? "view_in_ar" : "layers",
940
+ size: 16
941
+ }),
942
+ /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
943
+ className: "power-logs-option-text",
944
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: item ? item.pod : option.value ? "All pods" : option.label }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [option.value ? `Container · ${item?.container ?? option.value.slice(10)}` : "Every pod and container", item?.previous ? " · Previous" : ""] })]
945
+ }),
946
+ /* @__PURE__ */ require_jsx_runtime.jsx("span", {
947
+ className: "power-logs-source-check",
948
+ "aria-hidden": "true",
949
+ children: source === option.value && /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
950
+ material: "check",
951
+ size: 16
952
+ })
953
+ })
954
+ ]
955
+ });
956
+ },
957
+ value: source,
958
+ onChange: setSource,
959
+ options: [
960
+ {
961
+ value: "",
962
+ label: `All sources (${sources.length})`
963
+ },
964
+ ...[...new Set(sources.map((item) => item.container))].map((container) => ({
965
+ value: `container:${container}`,
966
+ label: `All pods / ${container}`
967
+ })),
968
+ ...[...new Set(sources.map(sourceKey))].map((value) => ({
969
+ value,
970
+ label: value
971
+ }))
972
+ ]
973
+ }),
974
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
975
+ type: "button",
976
+ disabled: !sources.length || busy || capturePending,
977
+ "aria-pressed": live,
978
+ onClick: toggleLive,
979
+ children: [
980
+ /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
981
+ material: live ? "pause" : "play_arrow",
982
+ size: 16
983
+ }),
984
+ " ",
985
+ live ? "Pause" : "Live"
986
+ ]
987
+ }),
988
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
989
+ type: "button",
990
+ "aria-pressed": follow,
991
+ onClick: () => setFollow(!follow),
992
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
993
+ material: "vertical_align_bottom",
994
+ size: 16
995
+ }), " Follow tail"]
996
+ }),
997
+ busy && /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
998
+ onClick: () => historyController.current?.abort(),
999
+ type: "button",
1000
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1001
+ material: "stop",
1002
+ size: 16
1003
+ }), " Stop loading"]
1004
+ })
1005
+ ]
1006
+ }),
1007
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1008
+ className: "power-logs-muted power-logs-status",
1009
+ role: "status",
1010
+ title: `${status} · Viewer retains up to ${limits.liveRows.toLocaleString()} lines / ${limits.liveBytes / 1048576} MiB`,
1011
+ children: [
1012
+ /* @__PURE__ */ require_jsx_runtime.jsxs("span", { children: [filtered.length.toLocaleString(), " matching lines"] }),
1013
+ " · ",
1014
+ status,
1015
+ liveCoverage && live ? ` · ${liveCoverage.active}/${liveCoverage.total} active sources` : "",
1016
+ " · Viewer retains up to ",
1017
+ limits.liveRows.toLocaleString(),
1018
+ " lines / ",
1019
+ limits.liveBytes / 1048576,
1020
+ " MiB"
1021
+ ]
1022
+ }),
1023
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1024
+ className: "power-logs-workspace",
1025
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(LogRows, {
1026
+ records: filtered,
1027
+ selected: selected?.key,
1028
+ onSelect: setSelected,
1029
+ follow,
1030
+ setFollow,
1031
+ compact
1032
+ }), selected && /* @__PURE__ */ require_jsx_runtime.jsx(LogDetails, {
1033
+ record: selected,
1034
+ onClose: () => setSelected(void 0)
1035
+ })]
1036
+ })
1037
+ ] }) : /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1038
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1039
+ className: "power-logs-toolbar power-logs-snapshot-bar",
1040
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(LogSelect, {
1041
+ label: "Capture snapshot",
1042
+ onKeyDown: (event) => {
1043
+ if (event.key !== "Delete" || !event.altKey || (event.target.value ?? "").length > 0) return;
1044
+ const button = document.querySelector(".Select__menu .Select__option--is-focused [data-capture-delete]");
1045
+ if (button && !button.disabled) {
1046
+ event.preventDefault();
1047
+ event.stopPropagation();
1048
+ deleteCapture(button.dataset.captureDelete);
1049
+ }
1050
+ },
1051
+ renderOption: (option, context) => {
1052
+ const item = captures.find((item) => item.id === option.value);
1053
+ if (context !== "menu" || !item) return option.label;
1054
+ return /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1055
+ className: "power-logs-snapshot-option",
1056
+ title: option.label,
1057
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1058
+ className: "power-logs-option-text",
1059
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("strong", { children: item.target.name }), /* @__PURE__ */ require_jsx_runtime.jsxs("small", { children: [
1060
+ require_date_time.formatDateTime(item.createdAt),
1061
+ " · ",
1062
+ item.rows.toLocaleString(),
1063
+ " lines ·",
1064
+ " ",
1065
+ item.state[0].toUpperCase(),
1066
+ item.state.slice(1)
1067
+ ] })]
1068
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1069
+ type: "button",
1070
+ "aria-label": `Delete snapshot ${item.target.name}`,
1071
+ title: "Delete snapshot (Alt+Delete)",
1072
+ "data-capture-delete": item.id,
1073
+ onKeyDown: (event) => event.stopPropagation(),
1074
+ disabled: !!queryId || capturePending || deletingCount > 0 || item.state === "collecting",
1075
+ onMouseDown: (event) => {
1076
+ event.preventDefault();
1077
+ event.stopPropagation();
1078
+ },
1079
+ onClick: (event) => {
1080
+ event.preventDefault();
1081
+ event.stopPropagation();
1082
+ deleteCapture(item.id);
1083
+ },
1084
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_extensions.Renderer.Component.Icon, {
1085
+ material: "delete_outline",
1086
+ size: 16,
1087
+ tooltip: "Delete snapshot",
1088
+ tooltipOverrideDisabled: true
1089
+ })
1090
+ })]
1091
+ });
1092
+ },
1093
+ value: captureId,
1094
+ disabled: !!queryId || capturePending || deletingCount > 0,
1095
+ onChange: (value) => {
1096
+ setCaptureId(value);
1097
+ setResult(void 0);
1098
+ setResultEvidence(void 0);
1099
+ setSelectedSqlRow(void 0);
1100
+ const snapshot = captures.find((item) => item.id === value);
1101
+ if (snapshot) {
1102
+ setSql(examples[0][1]);
1103
+ runQuery(snapshot, examples[0][1]);
1104
+ }
1105
+ },
1106
+ options: [{
1107
+ value: "",
1108
+ label: "Choose a snapshot"
1109
+ }, ...captures.map((item) => ({
1110
+ value: item.id,
1111
+ label: `${item.target.name} · ${require_date_time.formatDateTime(item.createdAt)} · ${item.rows.toLocaleString()} lines · ${item.state[0].toUpperCase()}${item.state.slice(1)}`
1112
+ }))]
1113
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1114
+ type: "button",
1115
+ disabled: !captureId || !!queryId || deleting.current.has(captureId),
1116
+ title: "Export JSON Lines (NDJSON)",
1117
+ onClick: () => {
1118
+ client.export(captureId).catch(() => setError("Could not export capture"));
1119
+ },
1120
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1121
+ material: "download",
1122
+ size: 16
1123
+ }), " Export JSON"]
1124
+ })]
1125
+ }),
1126
+ !!chosenCapture?.notes.length && /* @__PURE__ */ require_jsx_runtime.jsxs("details", {
1127
+ className: "power-logs-snapshot-notes power-logs-muted",
1128
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("summary", { children: [
1129
+ "Snapshot notes (",
1130
+ chosenCapture.notes.length,
1131
+ ")"
1132
+ ] }), chosenCapture.notes.map((note) => /* @__PURE__ */ require_jsx_runtime.jsx("div", { children: note }, note))]
1133
+ }),
1134
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1135
+ className: "power-logs-toolbar power-logs-sql-tools",
1136
+ children: [
1137
+ /* @__PURE__ */ require_jsx_runtime.jsx("label", {
1138
+ htmlFor: "power-logs-sql",
1139
+ title: `Read-only SELECT · ${limits.queryMs / 1e3} s · ${limits.resultRows.toLocaleString()} rows · Schema: logs(timestamp INTEGER in milliseconds, pod, podUid, container, level, message, fieldsJson, previous INTEGER 0 or 1)`,
1140
+ children: "SQL query"
1141
+ }),
1142
+ /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1143
+ type: "button",
1144
+ disabled: !chosenCapture || !!queryId || capturePending || deleting.current.has(captureId) || chosenCapture.state === "collecting",
1145
+ onClick: () => void runQuery(),
1146
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1147
+ material: "play_arrow",
1148
+ size: 16
1149
+ }), " Run query"]
1150
+ }),
1151
+ queryId && /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1152
+ type: "button",
1153
+ onClick: () => {
1154
+ client.cancel(queryId).catch(() => setError("Could not cancel query"));
1155
+ },
1156
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1157
+ material: "stop",
1158
+ size: 16
1159
+ }), " Cancel"]
1160
+ }),
1161
+ examples.map(([title, query]) => /* @__PURE__ */ require_jsx_runtime.jsxs("button", {
1162
+ type: "button",
1163
+ onClick: () => setSql(query),
1164
+ children: [
1165
+ /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1166
+ material: title === "Recent" ? "history" : title === "Errors" ? "error_outline" : title === "Timeline" ? "timeline" : "data_object",
1167
+ size: 16
1168
+ }),
1169
+ " ",
1170
+ "SQL: ",
1171
+ title
1172
+ ]
1173
+ }, title))
1174
+ ]
1175
+ }),
1176
+ /* @__PURE__ */ require_jsx_runtime.jsx("textarea", {
1177
+ rows: 2,
1178
+ id: "power-logs-sql",
1179
+ value: sql,
1180
+ maxLength: 65536,
1181
+ onChange: (event) => setSql(event.target.value),
1182
+ spellCheck: false
1183
+ }),
1184
+ result ? /* @__PURE__ */ require_jsx_runtime.jsxs(require_jsx_runtime.Fragment, { children: [
1185
+ /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1186
+ className: "power-logs-toolbar power-logs-results-bar",
1187
+ children: /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1188
+ role: "status",
1189
+ className: "power-logs-muted",
1190
+ children: [
1191
+ result.rows.length,
1192
+ " rows · ",
1193
+ result.elapsedMs,
1194
+ " ms",
1195
+ result.truncated ? " · Result truncated" : ""
1196
+ ]
1197
+ })
1198
+ }),
1199
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1200
+ className: "power-logs-workspace",
1201
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1202
+ className: "power-logs-results",
1203
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("table", {
1204
+ style: { minWidth: result.columns.length > 3 ? result.columns.reduce((width, column) => width + sqlColumnWidth(column), 0) : void 0 },
1205
+ children: [
1206
+ /* @__PURE__ */ require_jsx_runtime.jsx("colgroup", { children: result.columns.map((column, i) => /* @__PURE__ */ require_jsx_runtime.jsx("col", { style: { width: result.columns.length > 3 ? sqlColumnWidth(column) : void 0 } }, i)) }),
1207
+ /* @__PURE__ */ require_jsx_runtime.jsx("thead", { children: /* @__PURE__ */ require_jsx_runtime.jsx("tr", { children: result.columns.map((column, i) => /* @__PURE__ */ require_jsx_runtime.jsx("th", {
1208
+ "data-column": column,
1209
+ children: column
1210
+ }, i)) }) }),
1211
+ /* @__PURE__ */ require_jsx_runtime.jsx("tbody", { children: result.rows.slice(resultPage * 50, (resultPage + 1) * 50).map((row, i) => /* @__PURE__ */ require_jsx_runtime.jsx("tr", {
1212
+ tabIndex: 0,
1213
+ "aria-label": `Open SQL row ${resultPage * 50 + i + 1}`,
1214
+ onClick: () => setSelectedSqlRow({
1215
+ ...require_power_logs_sql_row.sqlRowDetails(result.columns, row),
1216
+ row
1217
+ }),
1218
+ onKeyDown: (event) => {
1219
+ if (event.key === "Enter" || event.key === " ") {
1220
+ event.preventDefault();
1221
+ setSelectedSqlRow({
1222
+ ...require_power_logs_sql_row.sqlRowDetails(result.columns, row),
1223
+ row
1224
+ });
1225
+ }
1226
+ },
1227
+ children: row.map((cell, j) => /* @__PURE__ */ require_jsx_runtime.jsx("td", {
1228
+ "data-column": result.columns[j],
1229
+ "data-level": result.columns[j] === "level" ? String(cell) : void 0,
1230
+ children: /* @__PURE__ */ require_jsx_runtime.jsx("span", {
1231
+ className: "power-logs-cell",
1232
+ title: cell === null ? "NULL" : String(cell),
1233
+ children: cell === null ? "NULL" : String(cell)
1234
+ })
1235
+ }, j))
1236
+ }, i)) })
1237
+ ]
1238
+ }), result.rows.length === 0 && /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1239
+ className: "power-logs-empty",
1240
+ children: chosenCapture?.rows === 0 ? "This snapshot contains no log lines. Try a longer capture window." : "This query returned no rows. Adjust the query or choose SQL: Recent."
1241
+ })]
1242
+ }), selectedSqlRow && /* @__PURE__ */ require_jsx_runtime.jsx(LogDetails, {
1243
+ record: selectedSqlRow.record,
1244
+ jsonColumn: selectedSqlRow.jsonColumn,
1245
+ onClose: () => setSelectedSqlRow(void 0)
1246
+ })]
1247
+ }),
1248
+ result.rows.length > 50 && /* @__PURE__ */ require_jsx_runtime.jsxs("nav", {
1249
+ className: "power-logs-toolbar power-logs-pagination",
1250
+ "aria-label": "SQL result pages",
1251
+ children: [
1252
+ /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1253
+ type: "button",
1254
+ "aria-label": "Previous page",
1255
+ title: "Previous page",
1256
+ disabled: resultPage === 0,
1257
+ onClick: () => {
1258
+ setResultPage(resultPage - 1);
1259
+ setSelectedSqlRow(void 0);
1260
+ },
1261
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1262
+ material: "chevron_left",
1263
+ size: 16
1264
+ })
1265
+ }),
1266
+ /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1267
+ className: "power-logs-muted",
1268
+ children: [
1269
+ "Page ",
1270
+ resultPage + 1,
1271
+ " / ",
1272
+ Math.max(1, Math.ceil(result.rows.length / 50))
1273
+ ]
1274
+ }),
1275
+ /* @__PURE__ */ require_jsx_runtime.jsx("button", {
1276
+ type: "button",
1277
+ "aria-label": "Next page",
1278
+ title: "Next page",
1279
+ disabled: (resultPage + 1) * 50 >= result.rows.length,
1280
+ onClick: () => {
1281
+ setResultPage(resultPage + 1);
1282
+ setSelectedSqlRow(void 0);
1283
+ },
1284
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1285
+ material: "chevron_right",
1286
+ size: 16
1287
+ })
1288
+ })
1289
+ ]
1290
+ })
1291
+ ] }) : /* @__PURE__ */ require_jsx_runtime.jsx("div", {
1292
+ className: "power-logs-results power-logs-empty",
1293
+ children: queryId ? "Running query…" : captures.length === 0 ? "Capture logs to create a temporary snapshot for SQL." : "Choose a snapshot to preview its logs."
1294
+ })
1295
+ ] }),
1296
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1297
+ className: "power-logs-muted power-logs-footer",
1298
+ children: [/* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1299
+ className: "power-logs-caption",
1300
+ title: `Gentle collection: ${limits.requestsPerSecond} requests/s, ${limits.bytesPerSecond / 1048576} MiB/s. Captures: ${limits.captureSourceLines.toLocaleString()} lines / ${limits.captureSourceBytes / 1048576} MiB per source, ${limits.captureBytes / 1048576} MiB total. Up to ${limits.captures} snapshots retained for ${limits.ttlMs / 6e4} minutes.`,
1301
+ children: [
1302
+ "Gentle collection · ",
1303
+ limits.requestsPerSecond,
1304
+ " requests/s · ",
1305
+ limits.bytesPerSecond / 1048576,
1306
+ " MiB/s · Captures limited to ",
1307
+ limits.captureSourceLines.toLocaleString(),
1308
+ " lines / ",
1309
+ limits.captureSourceBytes / 1048576,
1310
+ " MiB per source and ",
1311
+ limits.captureBytes / 1048576,
1312
+ " MiB total · Up to ",
1313
+ limits.captures,
1314
+ " temporary snapshots, retained for ",
1315
+ limits.ttlMs / 6e4,
1316
+ " min"
1317
+ ]
1318
+ }), collectionNotes.length > 0 && /* @__PURE__ */ require_jsx_runtime.jsxs("span", {
1319
+ role: "note",
1320
+ className: "power-logs-notice power-logs-collection-note",
1321
+ title: collectionNotes.join("\n"),
1322
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx(Icon, {
1323
+ material: "warning_amber",
1324
+ size: 14
1325
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("span", { children: collectionNotes.join(" · ") })]
1326
+ })]
1327
+ })
1328
+ ]
1329
+ });
1330
+ }
1331
+ var ObservedWorkspace = require__global_external_mobx_react.observer(Workspace);
1332
+ var PowerLogsPage = require__global_external_mobx_react.observer(() => {
1333
+ const enabled = require_logs_preferences_store.LogsPreferencesStore.getInstanceOrCreate().enabled;
1334
+ const clusterId = require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
1335
+ if (!enabled || !clusterId) return /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
1336
+ className: "power-logs",
1337
+ children: [
1338
+ /* @__PURE__ */ require_jsx_runtime.jsx("style", { children: require_power_logs_css.powerLogsStyles }),
1339
+ /* @__PURE__ */ require_jsx_runtime.jsx("h1", { children: "Power Logs" }),
1340
+ /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Enable Power Logs in Power settings and open a cluster to view logs." })
1341
+ ]
1342
+ });
1343
+ const contextId = require_resource_search_client.searchContextId();
1344
+ return /* @__PURE__ */ require_jsx_runtime.jsx(ObservedWorkspace, {
1345
+ clusterId,
1346
+ contextId
1347
+ }, `${clusterId}:${contextId}`);
1348
+ });
1349
+ //#endregion
1350
+ exports.PowerLogsPage = PowerLogsPage;
1351
+
1352
+ //# sourceMappingURL=power-logs.js.map