@64mb/freelens-power 1.3.0 → 1.5.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 (58) hide show
  1. package/README.md +4 -4
  2. package/out/main/common/resource-search.js +61 -0
  3. package/out/main/common/resource-search.js.map +1 -0
  4. package/out/main/common/store/monitoring-preferences-store.js +10 -0
  5. package/out/main/common/store/monitoring-preferences-store.js.map +1 -1
  6. package/out/main/index.js +2 -0
  7. package/out/main/index.js.map +1 -1
  8. package/out/main/resource-search-database.js +209 -0
  9. package/out/main/resource-search-database.js.map +1 -0
  10. package/out/main/resource-search-worker.js +56 -0
  11. package/out/main/resource-search-worker.js.map +1 -0
  12. package/out/main/resource-search.js +155 -0
  13. package/out/main/resource-search.js.map +1 -0
  14. package/out/renderer/appearance.js +1 -1
  15. package/out/renderer/appearance.js.map +1 -1
  16. package/out/renderer/details/resource-health-details.js +8 -4
  17. package/out/renderer/details/resource-health-details.js.map +1 -1
  18. package/out/renderer/diagnostics-state.js +7 -5
  19. package/out/renderer/diagnostics-state.js.map +1 -1
  20. package/out/renderer/diagnostics.css.js +2 -2
  21. package/out/renderer/diagnostics.css.js.map +1 -1
  22. package/out/renderer/diagnostics.js +127 -51
  23. package/out/renderer/diagnostics.js.map +1 -1
  24. package/out/renderer/gateway-relationships-client.js +107 -0
  25. package/out/renderer/gateway-relationships-client.js.map +1 -0
  26. package/out/renderer/index.js +2 -2
  27. package/out/renderer/index.js.map +1 -1
  28. package/out/renderer/monitoring.js +15 -0
  29. package/out/renderer/monitoring.js.map +1 -1
  30. package/out/renderer/preferences/monitoring-preferences.js +11 -1
  31. package/out/renderer/preferences/monitoring-preferences.js.map +1 -1
  32. package/out/renderer/preferences/preferences.js +16 -6
  33. package/out/renderer/preferences/preferences.js.map +1 -1
  34. package/out/renderer/resource-relationships.css.js +1 -1
  35. package/out/renderer/resource-relationships.css.js.map +1 -1
  36. package/out/renderer/resource-relationships.js +71 -18
  37. package/out/renderer/resource-relationships.js.map +1 -1
  38. package/out/renderer/resource-search-client.js +116 -0
  39. package/out/renderer/resource-search-client.js.map +1 -0
  40. package/out/renderer/resource-search-discovery.js +79 -0
  41. package/out/renderer/resource-search-discovery.js.map +1 -0
  42. package/out/renderer/resource-search-model.js +2 -1
  43. package/out/renderer/resource-search-model.js.map +1 -1
  44. package/out/renderer/resource-search-routing.js +78 -0
  45. package/out/renderer/resource-search-routing.js.map +1 -0
  46. package/out/renderer/resource-search-sync.js +199 -0
  47. package/out/renderer/resource-search-sync.js.map +1 -0
  48. package/out/renderer/resource-search.js +279 -54
  49. package/out/renderer/resource-search.js.map +1 -1
  50. package/out/renderer/src/common/gateway-relationships.js +122 -0
  51. package/out/renderer/src/common/gateway-relationships.js.map +1 -0
  52. package/out/renderer/src/common/resource-relationships.js +8 -0
  53. package/out/renderer/src/common/resource-relationships.js.map +1 -1
  54. package/out/renderer/src/common/resource-search.js +6 -0
  55. package/out/renderer/src/common/resource-search.js.map +1 -0
  56. package/out/renderer/src/common/store/monitoring-preferences-store.js +10 -0
  57. package/out/renderer/src/common/store/monitoring-preferences-store.js.map +1 -1
  58. package/package.json +6 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-search-sync.js","names":[],"sources":["../../src/renderer/resource-search-sync.ts"],"sourcesContent":["import {\n collectionPath,\n discoverSearchCollections,\n type MetadataPage,\n projectMetadataPage,\n type SearchCollection,\n type SearchTransport,\n searchErrorStatus,\n} from \"./resource-search-discovery\";\n\nimport type { SearchDocument } from \"../common/resource-search\";\n\nexport interface SearchSink {\n begin(scope: string, scanId: string): Promise<unknown>;\n append(scope: string, scanId: string, documents: SearchDocument[]): Promise<unknown>;\n complete(scope: string, scanId: string): Promise<unknown>;\n invalidate(scope: string): Promise<unknown>;\n reconcile?(resources: string[]): Promise<unknown>;\n}\nexport interface SearchCoverage {\n scanned: number;\n discovered: number;\n denied: number;\n unavailable: number;\n unsupported: number;\n indexing: boolean;\n message?: string;\n kinds?: string[];\n namespaces?: string[];\n completedAt?: number;\n storageFull?: boolean;\n}\nexport const SEARCH_STORAGE_FULL_MESSAGE = \"Search storage is full; clear the local search cache to continue\";\nexport const isSearchStorageFull = (error: unknown): boolean =>\n error instanceof Error && error.message.endsWith(SEARCH_STORAGE_FULL_MESSAGE);\nconst metadataAccept = { Accept: \"application/json;as=PartialObjectMetadataList;g=meta.k8s.io;v=v1\" };\nexport function createSearchSynchronizer(options: {\n transport: SearchTransport;\n sink: SearchSink;\n signal: AbortSignal;\n onStatus(status: SearchCoverage): void;\n namespaces?: () => string[];\n delay?: (milliseconds: number) => Promise<void>;\n refreshMs?: number;\n}) {\n const { transport, sink, signal } = options;\n const status: SearchCoverage = {\n scanned: 0,\n discovered: 0,\n denied: 0,\n unavailable: 0,\n unsupported: 0,\n indexing: true,\n };\n const emit = () => options.onStatus({ ...status });\n const delay =\n options.delay ??\n ((milliseconds: number) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) {\n resolve();\n return;\n }\n const done = () => {\n clearTimeout(timer);\n signal.removeEventListener(\"abort\", done);\n resolve();\n };\n const timer = setTimeout(done, milliseconds);\n signal.addEventListener(\"abort\", done, { once: true });\n }));\n let sequence = 0;\n let nextRequest = 0;\n const request = async <T>(\n path: string,\n query?: Record<string, string | number>,\n headers?: Record<string, string>,\n ): Promise<T> => {\n await delay(Math.max(0, nextRequest - Date.now()));\n if (signal.aborted) throw new Error(\"Search synchronization canceled\");\n nextRequest = Date.now() + 500;\n for (let attempt = 0; ; attempt++) {\n try {\n return await transport.get<T>(path, query, headers);\n } catch (error) {\n const code = searchErrorStatus(error);\n if (signal.aborted || attempt >= 3 || (code !== 429 && code < 500)) throw error;\n const retry = (error as { headers?: { get?(name: string): string | null }; retryAfter?: string }).retryAfter;\n const retryMilliseconds = retry\n ? /^\\d+$/.test(retry)\n ? Number(retry) * 1000\n : Date.parse(retry) - Date.now()\n : NaN;\n await delay(\n Number.isFinite(retryMilliseconds)\n ? Math.min(86400000, Math.max(0, retryMilliseconds))\n : Math.min(30000, 1000 * 2 ** attempt) + Math.random() * 250,\n );\n }\n }\n };\n const limited: SearchTransport = { get: request };\n const scan = async (type: SearchCollection, namespace?: string): Promise<void> => {\n const scope = `${type.group}/${type.resource}/${namespace ?? \"*\"}`;\n for (let restart = 0; restart < 2; restart++) {\n const scanId = `${Date.now()}-${++sequence}`;\n await sink.begin(scope, scanId);\n let continuation = \"\";\n let pageSize = 250;\n const seen = new Set<string>();\n try {\n do {\n let page: MetadataPage;\n for (;;) {\n const query = { limit: pageSize, ...(continuation ? { continue: continuation } : {}) };\n try {\n try {\n page = await request<MetadataPage>(collectionPath(type, namespace), query, metadataAccept);\n } catch (error) {\n if (searchErrorStatus(error) !== 406 || !type.group) throw error;\n page = await request<MetadataPage>(collectionPath(type, namespace), query, {\n Accept: \"application/json\",\n });\n }\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || pageSize === 1) throw error;\n pageSize = Math.max(1, Math.floor(pageSize / 2));\n }\n }\n if (signal.aborted) return;\n const documents = projectMetadataPage(type, page);\n status.namespaces = [\n ...new Set([\n ...(status.namespaces ?? []),\n ...documents.flatMap((document) => (document.namespace ? [document.namespace] : [])),\n ]),\n ].sort();\n await sink.append(scope, scanId, documents);\n continuation = page.metadata?.continue ?? \"\";\n if (continuation && seen.has(continuation)) throw new Error(\"Repeated inventory continuation\");\n seen.add(continuation);\n } while (continuation);\n if (!signal.aborted) await sink.complete(scope, scanId);\n return;\n } catch (error) {\n const code = searchErrorStatus(error);\n if (code === 410 && restart === 0) continue;\n if (code === 403) await sink.invalidate(scope);\n throw error;\n }\n }\n };\n return {\n async run(): Promise<void> {\n try {\n while (!signal.aborted) {\n status.indexing = true;\n status.scanned = 0;\n status.denied = 0;\n status.unavailable = 0;\n status.unsupported = 0;\n status.message = undefined;\n emit();\n let discoveryFailures = 0;\n const collections = await discoverSearchCollections(limited, () => {\n discoveryFailures++;\n status.unavailable++;\n emit();\n });\n if (signal.aborted) return;\n if (discoveryFailures === 0)\n await sink.reconcile?.(collections.map((type) => `${type.group}/${type.resource}`));\n else status.message = \"API discovery incomplete; cached resource types may be stale\";\n status.kinds = [...new Set(collections.map((type) => type.kind))].sort();\n status.discovered = collections.length;\n emit();\n let fallbackNamespaces: string[] | undefined;\n for (const type of collections) {\n if (signal.aborted) return;\n try {\n await scan(type);\n } catch (error) {\n if (signal.aborted) return;\n if (isSearchStorageFull(error)) throw error;\n if (searchErrorStatus(error) === 403) {\n status.denied++;\n if (type.namespaced) {\n let namespaces = fallbackNamespaces ?? options.namespaces?.() ?? [];\n if (fallbackNamespaces === undefined) {\n try {\n const namespaceType: SearchCollection = {\n group: \"\",\n version: \"v1\",\n resource: \"namespaces\",\n kind: \"Namespace\",\n namespaced: false,\n aliases: [],\n };\n let token = \"\";\n let limit = 250;\n const seen = new Set<string>();\n do {\n let page: MetadataPage;\n for (;;) {\n try {\n page = await request<MetadataPage>(\n collectionPath(namespaceType),\n { limit, ...(token ? { continue: token } : {}) },\n metadataAccept,\n );\n break;\n } catch (error) {\n if (!(error as { oversized?: boolean }).oversized || limit === 1) throw error;\n limit = Math.max(1, Math.floor(limit / 2));\n }\n }\n namespaces = [\n ...new Set([\n ...namespaces,\n ...(page.items ?? []).flatMap((item) => (item.metadata?.name ? [item.metadata.name] : [])),\n ]),\n ];\n token = page.metadata?.continue ?? \"\";\n if (token && seen.has(token)) throw new Error(\"Repeated namespace continuation\");\n seen.add(token);\n } while (token);\n } catch {\n status.message = \"Namespace discovery denied or incomplete; add a known namespace to continue\";\n }\n fallbackNamespaces = namespaces;\n }\n for (const namespace of namespaces) {\n if (signal.aborted) return;\n try {\n await scan(type, namespace);\n } catch (failure) {\n if (isSearchStorageFull(failure)) throw failure;\n if (searchErrorStatus(failure) === 403) status.denied++;\n else status.unavailable++;\n }\n }\n }\n } else if (searchErrorStatus(error) === 406) status.unsupported++;\n else status.unavailable++;\n }\n status.scanned++;\n emit();\n }\n status.indexing = false;\n status.completedAt = Date.now();\n emit();\n await delay(options.refreshMs ?? 10 * 60 * 1000);\n }\n } catch (error) {\n if (!isSearchStorageFull(error)) throw error;\n status.indexing = false;\n status.storageFull = true;\n status.message = SEARCH_STORAGE_FULL_MESSAGE;\n emit();\n }\n },\n };\n}\n"],"mappings":"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"}
@@ -1,13 +1,18 @@
1
1
  const require_extensions = require("./_virtual/_global-external_@freelensapp/extensions.js");
2
2
  const require__global_external_mobx = require("./_virtual/_global-external_mobx.js");
3
+ const require__global_external_mobx_react = require("./_virtual/_global-external_mobx-react.js");
4
+ const require_monitoring_preferences_store = require("./src/common/store/monitoring-preferences-store.js");
3
5
  const require__global_external_react = require("./_virtual/_global-external_react.js");
4
6
  const require__global_external_react_dom = require("./_virtual/_global-external_react-dom.js");
5
7
  const require_jsx_runtime = require("./_virtual/_global-external_react/jsx-runtime.js");
6
8
  const require_ai_icon = require("./ai-icon.js");
7
9
  const require_ai_diagnostic_handoff = require("./ai-diagnostic-handoff.js");
8
10
  const require_diagnostic_explain_button = require("./diagnostic-explain-button.js");
11
+ const require_resource_search_client = require("./resource-search-client.js");
12
+ const require_resource_search_routing = require("./resource-search-routing.js");
9
13
  const require_resource_search_fields = require("./resource-search-fields.js");
10
14
  const require_resource_search_model = require("./resource-search-model.js");
15
+ const require_resource_search_sync = require("./resource-search-sync.js");
11
16
  //#region src/renderer/resource-search.tsx
12
17
  var OPEN = "power-resource-search";
13
18
  var activeId = () => require_extensions.Renderer.Catalog.activeCluster.get()?.metadata.uid;
@@ -74,16 +79,16 @@ var inventory = () => [
74
79
  ]
75
80
  ];
76
81
  var icons = new Map(inventory().map(([kind, , icon]) => [kind, icon]));
77
- function project(kind, objects) {
78
- return objects.slice(0, 500).flatMap((object) => object.metadata.name ? [{
79
- kind,
80
- name: object.metadata.name,
81
- namespace: object.metadata.namespace,
82
- labels: Object.fromEntries(Object.entries(object.metadata.labels ?? {}).filter((entry) => typeof entry[1] === "string")),
83
- path: object.selfLink,
84
- fields: require_resource_search_fields.projectSearchFields(kind, object)
85
- }] : []);
86
- }
82
+ require__global_external_mobx_react.observer(() => {
83
+ if (!require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate().resourceSearchEnabled) return null;
84
+ return /* @__PURE__ */ require_jsx_runtime.jsx("button", {
85
+ type: "button",
86
+ title: "Search resources (double Shift)",
87
+ "aria-label": "Search resources",
88
+ onClick: () => window.dispatchEvent(new Event(OPEN)),
89
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "search" })
90
+ });
91
+ });
87
92
  function SearchSnippet({ field, query }) {
88
93
  if (!field) return null;
89
94
  const token = require_resource_search_model.queryTokens(query).find((token) => field.text.toLocaleLowerCase().includes(token));
@@ -104,9 +109,13 @@ function SearchSnippet({ field, query }) {
104
109
  }
105
110
  function SearchDialog({ close, clusterId }) {
106
111
  const [query, setQuery] = require__global_external_react.useState("");
112
+ const [knownNamespace, setKnownNamespace] = require__global_external_react.useState("");
113
+ const [kindFilter, setKindFilter] = require__global_external_react.useState("");
114
+ const [namespaceFilter, setNamespaceFilter] = require__global_external_react.useState("");
107
115
  const [resources, setResources] = require__global_external_react.useState([]);
108
116
  const [loading, setLoading] = require__global_external_react.useState(true);
109
117
  const [failed, setFailed] = require__global_external_react.useState(0);
118
+ const [cachedCount, setCachedCount] = require__global_external_react.useState();
110
119
  const [selectedPath, setSelectedPath] = require__global_external_react.useState();
111
120
  const [error, setError] = require__global_external_react.useState("");
112
121
  const dialog = require__global_external_react.useRef(null);
@@ -114,55 +123,73 @@ function SearchDialog({ close, clusterId }) {
114
123
  const searchIndex = require__global_external_react.useMemo(() => require_resource_search_model.createResourceSearchIndex(resources), [resources]);
115
124
  const matches = searchIndex.search(query);
116
125
  const selectedResource = matches.find((resource) => resource.path === selectedPath) ?? matches[0];
117
- const results = matches.slice(0, 100);
126
+ const results = matches;
118
127
  if (selectedResource && !results.includes(selectedResource)) results[results.length - 1] = selectedResource;
119
128
  const selected = results.findIndex((resource) => resource.path === selectedResource?.path);
120
129
  require__global_external_react.useEffect(() => {
121
130
  if (selectedPath !== void 0 && selectedPath !== selectedResource?.path) setSelectedPath(void 0);
122
131
  }, [selectedPath, selectedResource?.path]);
132
+ const [coverage, setCoverage] = require__global_external_react.useState(currentCoverage);
133
+ const [limit, setLimit] = require__global_external_react.useState(100);
134
+ const [hasMore, setHasMore] = require__global_external_react.useState(false);
123
135
  require__global_external_react.useEffect(() => {
124
136
  const previous = document.activeElement;
125
137
  input.current?.focus();
126
- const controller = new AbortController();
127
- const timeout = window.setTimeout(() => controller.abort(), 15e3);
138
+ const update = () => setCoverage({ ...currentCoverage });
139
+ window.addEventListener("power-search-coverage", update);
140
+ return () => {
141
+ window.removeEventListener("power-search-coverage", update);
142
+ if (previous?.isConnected) previous.focus();
143
+ };
144
+ }, []);
145
+ require__global_external_react.useEffect(() => {
128
146
  let live = true;
129
- const sources = inventory();
130
- const parts = sources.map(([kind, store]) => project(kind, store.items));
131
- setResources(parts.flat());
132
- let next = 0;
133
- const worker = async () => {
134
- while (next < sources.length && live && activeId() === clusterId) {
135
- const index = next++;
136
- const [kind, store] = sources[index];
137
- try {
138
- const objects = await store.api.list({
139
- namespace: "",
140
- reqInit: { signal: controller.signal }
141
- }, { limit: 500 });
142
- if (!live || activeId() !== clusterId) return;
143
- parts[index] = project(kind, objects ?? []);
144
- setResources(parts.flat());
145
- } catch {
146
- if (live && activeId() === clusterId) setFailed((count) => count + 1);
147
+ const client = require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId());
148
+ const timer = window.setTimeout(() => {
149
+ client.query(query, limit, {
150
+ ...kindFilter ? { kind: coverage.kinds?.find((kind) => kind.toLocaleLowerCase() === kindFilter.toLocaleLowerCase()) ?? kindFilter } : {},
151
+ ...namespaceFilter ? { namespace: namespaceFilter === "(cluster)" ? "" : namespaceFilter } : {}
152
+ }).then((result) => {
153
+ if (!live || activeId() !== clusterId) return;
154
+ setResources(result.documents);
155
+ setHasMore(result.hasMore);
156
+ setLoading(false);
157
+ setFailed(0);
158
+ client.status?.().then((status) => {
159
+ if (live) setCachedCount(status.count);
160
+ }).catch(() => {});
161
+ }).catch(() => {
162
+ if (live) {
163
+ setFailed(1);
164
+ setLoading(false);
147
165
  }
148
- }
149
- };
150
- Promise.all([worker(), worker()]).then(() => {
151
- if (live) setLoading(false);
152
- });
166
+ });
167
+ }, 100);
153
168
  return () => {
154
169
  live = false;
155
- window.clearTimeout(timeout);
156
- controller.abort();
157
- if (previous?.isConnected) previous.focus();
170
+ window.clearTimeout(timer);
158
171
  };
159
- }, [clusterId]);
172
+ }, [
173
+ clusterId,
174
+ query,
175
+ limit,
176
+ kindFilter,
177
+ namespaceFilter,
178
+ coverage.scanned,
179
+ coverage.indexing
180
+ ]);
160
181
  require__global_external_react.useEffect(() => {
161
182
  document.getElementById(`power-resource-result-${selected}`)?.scrollIntoView?.({ block: "nearest" });
162
183
  }, [selected, selectedResource?.path]);
184
+ const clearCache = () => {
185
+ require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId()).clear().then(() => {
186
+ setResources([]);
187
+ restartSearchSync();
188
+ }).catch(() => setError("Could not clear the local search cache. Try again."));
189
+ };
163
190
  const open = (resource) => {
164
191
  if (activeId() !== clusterId) return;
165
- Promise.resolve().then(() => require_extensions.Renderer.Navigation.showDetails(resource.path)).then(() => {
192
+ Promise.resolve().then(() => require_resource_search_routing.openSearchResource(resource)).then(() => {
166
193
  if (activeId() === clusterId) close();
167
194
  }).catch(() => setError("Could not open this resource. Try again or refresh the inventory."));
168
195
  };
@@ -186,7 +213,7 @@ function SearchDialog({ close, clusterId }) {
186
213
  close();
187
214
  }
188
215
  if (event.key === "Tab") {
189
- const controls = Array.from(dialog.current?.querySelectorAll("input, button:not(:disabled)") ?? []);
216
+ const controls = Array.from(dialog.current?.querySelectorAll("input, button:not(:disabled), select") ?? []);
190
217
  const index = controls.indexOf(document.activeElement);
191
218
  if (event.shiftKey && index <= 0) {
192
219
  event.preventDefault();
@@ -211,11 +238,23 @@ function SearchDialog({ close, clusterId }) {
211
238
  material: "search",
212
239
  size: 18
213
240
  }), "Search resources"]
214
- }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
215
- type: "button",
216
- "aria-label": "Close resource search",
217
- onClick: close,
218
- children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "close" })
241
+ }), /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
242
+ style: {
243
+ display: "flex",
244
+ gap: 4
245
+ },
246
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("button", {
247
+ type: "button",
248
+ "aria-label": "Refresh resource inventory",
249
+ title: "Refresh resource inventory",
250
+ onClick: () => restartSearchSync(),
251
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "refresh" })
252
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
253
+ type: "button",
254
+ "aria-label": "Close resource search",
255
+ onClick: close,
256
+ children: /* @__PURE__ */ require_jsx_runtime.jsx(require_ai_icon.AiIcon, { material: "close" })
257
+ })]
219
258
  })]
220
259
  }),
221
260
  /* @__PURE__ */ require_jsx_runtime.jsx("input", {
@@ -240,14 +279,68 @@ function SearchDialog({ close, clusterId }) {
240
279
  }
241
280
  }
242
281
  }),
282
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
283
+ className: "power-resource-search-filters",
284
+ style: {
285
+ display: "flex",
286
+ gap: 8,
287
+ flexWrap: "wrap"
288
+ },
289
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
290
+ list: "power-search-kinds",
291
+ "aria-label": "Filter by exact resource kind",
292
+ placeholder: "Kind (all types)",
293
+ value: kindFilter,
294
+ maxLength: 253,
295
+ onChange: (event) => {
296
+ setKindFilter(event.target.value);
297
+ setSelectedPath(void 0);
298
+ }
299
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("input", {
300
+ list: "power-search-namespaces",
301
+ "aria-label": "Filter by exact namespace",
302
+ placeholder: "Namespace (all)",
303
+ value: namespaceFilter,
304
+ maxLength: 253,
305
+ onChange: (event) => {
306
+ setNamespaceFilter(event.target.value);
307
+ setSelectedPath(void 0);
308
+ }
309
+ })]
310
+ }),
311
+ /* @__PURE__ */ require_jsx_runtime.jsx("datalist", {
312
+ id: "power-search-kinds",
313
+ children: (coverage.kinds ?? []).map((kind) => /* @__PURE__ */ require_jsx_runtime.jsx("option", { value: kind }, kind))
314
+ }),
315
+ /* @__PURE__ */ require_jsx_runtime.jsxs("datalist", {
316
+ id: "power-search-namespaces",
317
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("option", {
318
+ value: "(cluster)",
319
+ children: "Cluster scoped"
320
+ }), (coverage.namespaces ?? []).map((namespace) => /* @__PURE__ */ require_jsx_runtime.jsx("option", { value: namespace }, namespace))]
321
+ }),
243
322
  /* @__PURE__ */ require_jsx_runtime.jsxs("p", {
244
323
  role: "status",
245
324
  children: [
246
- loading ? "Loading supported kinds; cached results are available. " : "Supported kinds loaded. ",
247
- "Partial inventory: up to 500 objects per kind, subject to permissions. Secrets, ConfigMaps and custom resources are excluded. Full-text index covers safe metadata, images, selectors and status only.",
248
- failed > 0 && ` ${failed} kinds unavailable; cached results retained.`
325
+ coverage.indexing ? `Indexing ${coverage.scanned}/${coverage.discovered} types` : coverage.completedAt ? `Updated ${new Date(coverage.completedAt).toLocaleTimeString([], {
326
+ hour: "2-digit",
327
+ minute: "2-digit"
328
+ })}` : "Cached inventory",
329
+ cachedCount !== void 0 && ` · ${cachedCount.toLocaleString()} resources`,
330
+ coverage.denied > 0 && ` · ${coverage.denied} restricted scopes`,
331
+ coverage.unavailable + coverage.unsupported > 0 && ` · ${coverage.unavailable + coverage.unsupported} APIs unavailable`,
332
+ failed > 0 && " · Search cache unavailable"
249
333
  ]
250
334
  }),
335
+ (coverage.message || coverage.denied > 0 || coverage.unavailable + coverage.unsupported > 0) && /* @__PURE__ */ require_jsx_runtime.jsxs("details", { children: [/* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "Inventory coverage" }), /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: coverage.message ?? "Results include the resources your current permissions and available APIs allow." })] }),
336
+ coverage.storageFull && /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
337
+ role: "alert",
338
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Local search storage is full. Cached results are partial; indexing is paused." }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
339
+ type: "button",
340
+ onClick: clearCache,
341
+ children: "Clear local search cache"
342
+ })]
343
+ }),
251
344
  error && /* @__PURE__ */ require_jsx_runtime.jsx("p", {
252
345
  role: "alert",
253
346
  children: error
@@ -289,7 +382,7 @@ function SearchDialog({ close, clusterId }) {
289
382
  require_ai_diagnostic_handoff.explainDiagnosticFinding({
290
383
  clusterId,
291
384
  title: `${resource.kind}: ${resource.name}`,
292
- summary: `Resource selected from search. Kind: ${resource.kind}; namespace: ${resource.namespace ?? "cluster scoped"}; name: ${resource.name}. No live diagnostic data was collected.`,
385
+ summary: `Resource selected from search. API group: ${resource.group || "core"}; version: ${resource.version}; resource: ${resource.resource}; kind: ${resource.kind}; namespace: ${resource.namespace ?? "cluster scoped"}; name: ${resource.name}; UID: ${resource.uid ?? "unknown"}; path: ${resource.path}. No live diagnostic data was collected.`,
293
386
  resource: {
294
387
  kind: resource.kind,
295
388
  name: resource.name,
@@ -301,7 +394,43 @@ function SearchDialog({ close, clusterId }) {
301
394
  })]
302
395
  }, resource.path)), !results.length && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: loading ? "Searching…" : "No matching resources in the available inventory" })]
303
396
  }),
304
- matches.length > 100 && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Showing 100 matches. Refine your search." }),
397
+ hasMore && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
398
+ type: "button",
399
+ disabled: limit >= 1e3,
400
+ onClick: () => setLimit((value) => Math.min(1e3, value + 100)),
401
+ children: "Show more matches"
402
+ }),
403
+ /* @__PURE__ */ require_jsx_runtime.jsxs("details", { children: [
404
+ /* @__PURE__ */ require_jsx_runtime.jsx("summary", { children: "Search options" }),
405
+ /* @__PURE__ */ require_jsx_runtime.jsxs("div", {
406
+ style: {
407
+ display: "flex",
408
+ gap: 8,
409
+ flexWrap: "wrap"
410
+ },
411
+ children: [/* @__PURE__ */ require_jsx_runtime.jsx("input", {
412
+ "aria-label": "Known namespace for restricted access",
413
+ placeholder: "Known namespace",
414
+ value: knownNamespace,
415
+ maxLength: 63,
416
+ onChange: (event) => setKnownNamespace(event.target.value)
417
+ }), /* @__PURE__ */ require_jsx_runtime.jsx("button", {
418
+ type: "button",
419
+ disabled: !/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(knownNamespace),
420
+ onClick: () => {
421
+ extraNamespaces.add(knownNamespace);
422
+ restartSearchSync();
423
+ },
424
+ children: "Index namespace"
425
+ })]
426
+ }),
427
+ hasMore && limit >= 1e3 && /* @__PURE__ */ require_jsx_runtime.jsx("p", { children: "Showing up to 1000 candidates. Refine the name, type or namespace to continue." }),
428
+ !coverage.storageFull && /* @__PURE__ */ require_jsx_runtime.jsx("button", {
429
+ type: "button",
430
+ onClick: clearCache,
431
+ children: "Clear local search cache"
432
+ })
433
+ ] }),
305
434
  /* @__PURE__ */ require_jsx_runtime.jsx("p", {
306
435
  className: "power-resource-search-shortcuts",
307
436
  children: "↑↓ Navigate · Enter Open · Esc Close"
@@ -310,10 +439,98 @@ function SearchDialog({ close, clusterId }) {
310
439
  })
311
440
  });
312
441
  }
442
+ var currentCoverage = {
443
+ scanned: 0,
444
+ discovered: 0,
445
+ denied: 0,
446
+ unavailable: 0,
447
+ unsupported: 0,
448
+ indexing: true
449
+ };
450
+ var restartSearchSync = () => {};
451
+ var extraNamespaces = /* @__PURE__ */ new Set();
313
452
  function startResourceSearch() {
453
+ const preferences = require_monitoring_preferences_store.MonitoringPreferencesStore.getInstanceOrCreate();
454
+ let stop = preferences.resourceSearchEnabled ? startEnabledResourceSearch() : void 0;
455
+ const dispose = require__global_external_mobx.reaction(() => preferences.resourceSearchEnabled, (enabled) => {
456
+ stop?.();
457
+ stop = enabled ? startEnabledResourceSearch() : void 0;
458
+ });
459
+ return () => {
460
+ dispose();
461
+ stop?.();
462
+ stop = void 0;
463
+ };
464
+ }
465
+ function startEnabledResourceSearch() {
466
+ let syncAbort;
467
+ const sync = () => {
468
+ syncAbort?.abort();
469
+ require_resource_search_routing.closeSearchResourceFallback();
470
+ const clusterId = activeId();
471
+ if (!clusterId || window.top === window) return;
472
+ const phase = require_extensions.Renderer.Catalog.activeCluster.get()?.status?.phase;
473
+ if (phase && phase !== "connected") {
474
+ currentCoverage = {
475
+ ...currentCoverage,
476
+ indexing: false,
477
+ message: "Cluster disconnected; cached results may be stale"
478
+ };
479
+ window.dispatchEvent(new Event("power-search-coverage"));
480
+ return;
481
+ }
482
+ const controller = new AbortController();
483
+ syncAbort = controller;
484
+ const client = require_resource_search_client.createResourceSearchClient(clusterId, require_resource_search_client.searchContextId());
485
+ const liveStores = new Map(inventory().map(([kind, store]) => [kind, store]));
486
+ const liveObjects = /* @__PURE__ */ new Map();
487
+ const sink = {
488
+ ...client,
489
+ append: (scope, scanId, documents) => client.append(scope, scanId, documents.map((item) => {
490
+ const store = liveStores.get(item.kind);
491
+ if (!store) return item;
492
+ let objects = liveObjects.get(item.kind);
493
+ if (!objects) {
494
+ objects = new Map(store.items.map((object) => [object.selfLink, object]));
495
+ liveObjects.set(item.kind, objects);
496
+ }
497
+ const object = objects.get(item.path);
498
+ return object ? {
499
+ ...item,
500
+ fields: require_resource_search_fields.projectSearchFields(item.kind, object)
501
+ } : item;
502
+ }))
503
+ };
504
+ let wasIndexing = false;
505
+ require_resource_search_sync.createSearchSynchronizer({
506
+ transport: require_resource_search_client.createResourceSearchTransport(clusterId, controller.signal),
507
+ sink,
508
+ signal: controller.signal,
509
+ namespaces: () => [.../* @__PURE__ */ new Set([...require_extensions.Renderer.Catalog.activeCluster.get()?.spec?.accessibleNamespaces ?? [], ...extraNamespaces])],
510
+ onStatus: (status) => {
511
+ if (!controller.signal.aborted) {
512
+ if (status.indexing && !wasIndexing) liveObjects.clear();
513
+ wasIndexing = status.indexing;
514
+ currentCoverage = status;
515
+ window.dispatchEvent(new Event("power-search-coverage"));
516
+ }
517
+ }
518
+ }).run().catch(() => {
519
+ if (!controller.signal.aborted) {
520
+ currentCoverage = {
521
+ ...currentCoverage,
522
+ indexing: false,
523
+ unavailable: currentCoverage.unavailable + 1
524
+ };
525
+ window.dispatchEvent(new Event("power-search-coverage"));
526
+ }
527
+ });
528
+ };
529
+ restartSearchSync = sync;
530
+ sync();
314
531
  const container = document.createElement("div");
315
532
  const style = document.createElement("style");
316
- style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:12vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:60px}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:var(--tableSelectedBackground,#394b66);border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableHoveredBackground,#ffffff0b)}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;
533
+ style.textContent = `.power-resource-search-backdrop{position:fixed;inset:0;z-index:10000;background:#0007;display:flex;align-items:flex-start;justify-content:center;padding:8vh 20px}.power-resource-search-dialog{width:680px;max-width:100%;max-height:75vh;display:flex;flex-direction:column;padding:20px;gap:12px;background:var(--mainBackground,#222);color:var(--textColorPrimary,#eee);border:1px solid var(--borderColor,#555);border-radius:8px;box-shadow:0 16px 60px #0008}.power-resource-search-heading{display:flex;align-items:center;justify-content:space-between}.power-resource-search-filters input{flex:1;min-width:180px}.power-resource-search-dialog input{padding:12px;font-size:16px;background:var(--inputControlBackground,#333);color:inherit;border:1px solid var(--borderColor,#666);border-radius:4px}.power-resource-search-dialog p{margin:0;font-size:12px;color:var(--textColorSecondary,#bbb)}.power-resource-search-results{overflow:auto;min-height:180px;flex:1}.power-resource-search-row{display:flex;align-items:center;gap:12px;padding:4px;border-radius:4px}.power-resource-search-row{border-left:3px solid transparent}.power-resource-search-row.selected{background:color-mix(in srgb,var(--primary,#668fff) 16%,var(--mainBackground,#222));color:var(--tableSelectedRowColor,var(--textColorPrimary,#eee));border-left-color:var(--primary,#668fff)}.power-resource-search-row.selected .Icon{color:var(--primary,#8daaff)}.power-resource-search-row:hover{background:var(--tableBgcSelected,var(--contentColor,#292d32))}.power-resource-search-dialog button:hover:not(:disabled){background:#ffffff12;border-radius:4px}.power-resource-search-row>button:last-child{border:1px solid var(--borderColor,#555);border-radius:4px}.power-resource-search-shortcuts{padding-top:8px;border-top:1px solid var(--borderColor,#555)}.power-resource-search-dialog button{min-width:32px;min-height:32px;padding:6px;color:inherit;background:transparent;border:0;cursor:pointer}.power-resource-search-dialog button:disabled{opacity:.45;cursor:default}.power-resource-search-open{display:flex;align-items:center;gap:12px;flex:1;text-align:left;min-width:0}.power-resource-search-open span{min-width:0}.power-resource-search-open strong,.power-resource-search-open small{display:block;overflow-wrap:anywhere}.power-resource-search-open small{opacity:.7}.power-resource-search-row>button.power-resource-search-ai{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:34px;height:34px;min-width:34px;padding:0;background:var(--contentColor,#292d32);border:1px solid var(--borderColor,#666)}.power-resource-search-row.selected>button.power-resource-search-ai{background:var(--mainBackground,#202326);border-color:color-mix(in srgb,var(--textColorPrimary,#ddd) 55%,var(--borderColor,#666));box-shadow:0 1px 3px #0002}.power-resource-search-row.selected>button.power-resource-search-ai .Icon{color:var(--textColorAccent,#eee)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled){background:var(--contentColor,#30343a);border-color:var(--primary,#8daaff);color:var(--primary,#8daaff)}.power-resource-search-row>button.power-resource-search-ai:hover:not(:disabled) .Icon{color:inherit}.power-resource-search-dialog button:focus-visible{outline:2px solid var(--primary,#668fff)}`;
317
534
  document.head.append(style);
318
535
  document.body.append(container);
319
536
  let visible = false;
@@ -347,9 +564,17 @@ function startResourceSearch() {
347
564
  window.addEventListener("keydown", key);
348
565
  window.addEventListener("keyup", key);
349
566
  window.addEventListener(OPEN, show);
350
- const dispose = require__global_external_mobx.reaction(activeId, close);
567
+ const dispose = require__global_external_mobx.reaction(() => `${require_resource_search_client.searchContextId()}/${require_extensions.Renderer.Catalog.activeCluster.get()?.status?.phase}`, () => {
568
+ close();
569
+ extraNamespaces.clear();
570
+ sync();
571
+ });
351
572
  return () => {
352
573
  close();
574
+ syncAbort?.abort();
575
+ require_resource_search_routing.closeSearchResourceFallback();
576
+ restartSearchSync = () => {};
577
+ extraNamespaces.clear();
353
578
  dispose();
354
579
  window.removeEventListener("blur", resetShift);
355
580
  window.removeEventListener("keydown", key);