@acarmisc/backstage-plugin-ai-agents 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1000 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res, err) => function __init() {
9
+ if (err) throw err[0];
10
+ try {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ } catch (e) {
13
+ throw err = [e], e;
14
+ }
15
+ };
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+
38
+ // src/types.ts
39
+ function annotation(entity, key) {
40
+ return entity.metadata.annotations?.[`${AI_AGENT_ANNOTATION_PREFIX}/${key}`];
41
+ }
42
+ function parseFloatSafe(value) {
43
+ if (!value) return void 0;
44
+ const n = Number.parseFloat(value);
45
+ return Number.isFinite(n) ? n : void 0;
46
+ }
47
+ function parseCapabilities(raw) {
48
+ if (!raw) return [];
49
+ return raw.split(/[,\n]/).map((s) => s.trim()).filter(Boolean).map((token) => {
50
+ const [label, catRaw] = token.split(":").map((s) => s.trim());
51
+ const category = catRaw ? catRaw : void 0;
52
+ return {
53
+ label,
54
+ category: category && VALID_CATEGORIES.has(category) ? category : void 0
55
+ };
56
+ });
57
+ }
58
+ function entityRef(entity) {
59
+ const ns = entity.metadata.namespace ?? "default";
60
+ return `${entity.kind.toLowerCase()}:${ns}/${entity.metadata.name}`;
61
+ }
62
+ function purpose(entity) {
63
+ const explicit = annotation(entity, "purpose");
64
+ return (explicit ?? entity.metadata.description ?? "").trim();
65
+ }
66
+ function entityToAgent(entity, status) {
67
+ if (entity.spec?.type !== AI_AGENT_TYPE) return void 0;
68
+ const links = (entity.metadata.links ?? []).map((l) => ({
69
+ url: l.url,
70
+ title: l.title ?? l.url,
71
+ icon: l.icon
72
+ }));
73
+ return {
74
+ entityRef: entityRef(entity),
75
+ name: entity.metadata.name,
76
+ title: entity.metadata.title,
77
+ description: entity.metadata.description,
78
+ avatarUrl: annotation(entity, "avatar"),
79
+ owner: entity.spec?.owner,
80
+ system: entity.spec?.system,
81
+ lifecycle: entity.spec?.lifecycle,
82
+ version: annotation(entity, "version"),
83
+ purpose: purpose(entity),
84
+ runtime: {
85
+ runtime: annotation(entity, "runtime") ?? "custom",
86
+ runtimeHandle: annotation(entity, "runtime-handle"),
87
+ endpoint: annotation(entity, "endpoint"),
88
+ healthUrl: annotation(entity, "health")
89
+ },
90
+ billing: {
91
+ model: annotation(entity, "billing-model") ?? "free",
92
+ unitCost: parseFloatSafe(annotation(entity, "cost-per-1k")),
93
+ budget: parseFloatSafe(annotation(entity, "budget"))
94
+ },
95
+ capabilities: parseCapabilities(annotation(entity, "capabilities")),
96
+ tags: entity.metadata.tags ?? [],
97
+ links,
98
+ status,
99
+ rawEntity: entity
100
+ };
101
+ }
102
+ var AI_AGENT_TYPE, AI_AGENT_ANNOTATION_PREFIX, VALID_CATEGORIES;
103
+ var init_types = __esm({
104
+ "src/types.ts"() {
105
+ "use strict";
106
+ AI_AGENT_TYPE = "ai-agent";
107
+ AI_AGENT_ANNOTATION_PREFIX = "ai-agent.acarmisc.org";
108
+ VALID_CATEGORIES = /* @__PURE__ */ new Set([
109
+ "reasoning",
110
+ "retrieval",
111
+ "tools",
112
+ "vision",
113
+ "voice",
114
+ "data",
115
+ "safety"
116
+ ]);
117
+ }
118
+ });
119
+
120
+ // src/api.ts
121
+ var import_core_plugin_api, aiAgentsApiRef, AiAgentsApi;
122
+ var init_api = __esm({
123
+ "src/api.ts"() {
124
+ "use strict";
125
+ import_core_plugin_api = require("@backstage/core-plugin-api");
126
+ init_types();
127
+ aiAgentsApiRef = (0, import_core_plugin_api.createApiRef)({
128
+ id: "plugin.ai-agents.api"
129
+ });
130
+ AiAgentsApi = class {
131
+ constructor(opts, basePath = "/api/ai-agents") {
132
+ this.opts = opts;
133
+ this.basePath = basePath;
134
+ }
135
+ async listAgents() {
136
+ const result = await this.opts.catalogApi.getEntities({
137
+ filter: { kind: "Component", "spec.type": AI_AGENT_TYPE }
138
+ });
139
+ return result.items.map((e) => entityToAgent(e)).filter((a) => a !== void 0);
140
+ }
141
+ async getAgent(entityRef2) {
142
+ const entity = await this.opts.catalogApi.getEntityByRef(entityRef2);
143
+ return entity ? entityToAgent(entity) : void 0;
144
+ }
145
+ async getStatuses(entityRefs) {
146
+ if (!entityRefs.length) return {};
147
+ try {
148
+ const res = await this.opts.fetchApi.fetch(
149
+ `${this.basePath}/statuses?refs=${encodeURIComponent(entityRefs.join(","))}`
150
+ );
151
+ if (!res.ok) return {};
152
+ return await res.json();
153
+ } catch {
154
+ return {};
155
+ }
156
+ }
157
+ };
158
+ }
159
+ });
160
+
161
+ // src/hooks/useAgents.ts
162
+ function applyFilters(agents, filters) {
163
+ const search = filters.search.trim().toLowerCase();
164
+ return agents.filter((a) => {
165
+ if (search) {
166
+ const haystack = [a.name, a.title, a.description, a.purpose].filter(Boolean).join(" ").toLowerCase();
167
+ if (!haystack.includes(search)) return false;
168
+ }
169
+ if (filters.runtime.length && !filters.runtime.includes(a.runtime.runtime)) {
170
+ return false;
171
+ }
172
+ if (filters.capability.length) {
173
+ const caps = a.capabilities.map((c) => c.label);
174
+ if (!filters.capability.every((c) => caps.includes(c))) return false;
175
+ }
176
+ if (filters.lifecycle.length && !filters.lifecycle.includes(a.lifecycle ?? "")) {
177
+ return false;
178
+ }
179
+ if (filters.owner.length && !filters.owner.includes(a.owner ?? "")) {
180
+ return false;
181
+ }
182
+ return true;
183
+ });
184
+ }
185
+ function useAgents() {
186
+ const api = (0, import_core_plugin_api2.useApi)(aiAgentsApiRef);
187
+ const {
188
+ value: agents,
189
+ loading,
190
+ error,
191
+ retry
192
+ } = (0, import_react_use.useAsyncRetry)(() => api.listAgents(), [api]);
193
+ const [filters, setFilters] = (0, import_react.useState)(initialFilters);
194
+ const filtered = (0, import_react.useMemo)(
195
+ () => applyFilters(agents ?? [], filters),
196
+ [agents, filters]
197
+ );
198
+ const update = (0, import_react.useCallback)(
199
+ (patch) => setFilters((prev) => ({ ...prev, ...patch })),
200
+ []
201
+ );
202
+ const reset = (0, import_react.useCallback)(() => setFilters(initialFilters), []);
203
+ return {
204
+ agents: filtered,
205
+ allAgents: agents ?? [],
206
+ loading,
207
+ error,
208
+ retry,
209
+ filters,
210
+ update,
211
+ reset
212
+ };
213
+ }
214
+ var import_react, import_core_plugin_api2, import_react_use, initialFilters;
215
+ var init_useAgents = __esm({
216
+ "src/hooks/useAgents.ts"() {
217
+ "use strict";
218
+ import_react = require("react");
219
+ import_core_plugin_api2 = require("@backstage/core-plugin-api");
220
+ import_react_use = require("react-use");
221
+ init_api();
222
+ initialFilters = {
223
+ search: "",
224
+ runtime: [],
225
+ capability: [],
226
+ lifecycle: [],
227
+ owner: []
228
+ };
229
+ }
230
+ });
231
+
232
+ // src/components/AgentFilters.tsx
233
+ var import_react2, import_material, import_Search, import_Clear, SELECT_PROPS, AgentFiltersBar;
234
+ var init_AgentFilters = __esm({
235
+ "src/components/AgentFilters.tsx"() {
236
+ "use strict";
237
+ import_react2 = __toESM(require("react"));
238
+ import_material = require("@mui/material");
239
+ import_Search = __toESM(require("@mui/icons-material/Search"));
240
+ import_Clear = __toESM(require("@mui/icons-material/Clear"));
241
+ SELECT_PROPS = {
242
+ SelectProps: { multiple: true, renderValue: (v) => v.join(", ") || "All" }
243
+ };
244
+ AgentFiltersBar = ({
245
+ agents,
246
+ filters,
247
+ onChange,
248
+ onReset
249
+ }) => {
250
+ const runtimes = Array.from(new Set(agents.map((a) => a.runtime.runtime))).sort();
251
+ const capabilities = Array.from(
252
+ new Set(agents.flatMap((a) => a.capabilities.map((c) => c.label)))
253
+ ).sort();
254
+ const lifecycles = Array.from(new Set(agents.map((a) => a.lifecycle).filter(Boolean)));
255
+ const owners = Array.from(new Set(agents.map((a) => a.owner).filter(Boolean)));
256
+ const hasFilters = filters.search || filters.runtime.length || filters.capability.length || filters.lifecycle.length || filters.owner.length;
257
+ return /* @__PURE__ */ import_react2.default.createElement(import_material.Paper, { sx: { p: 1.5, mb: 2, display: "flex", gap: 1.5, flexWrap: "wrap", alignItems: "center" } }, /* @__PURE__ */ import_react2.default.createElement(
258
+ import_material.TextField,
259
+ {
260
+ size: "small",
261
+ placeholder: "Search agents\u2026",
262
+ value: filters.search,
263
+ onChange: (e) => onChange({ search: e.target.value }),
264
+ sx: { minWidth: 220, flexGrow: 1 },
265
+ InputProps: {
266
+ startAdornment: /* @__PURE__ */ import_react2.default.createElement(import_material.InputAdornment, { position: "start" }, /* @__PURE__ */ import_react2.default.createElement(import_Search.default, { fontSize: "small" })),
267
+ endAdornment: filters.search ? /* @__PURE__ */ import_react2.default.createElement(import_material.InputAdornment, { position: "end" }, /* @__PURE__ */ import_react2.default.createElement(import_material.IconButton, { size: "small", onClick: () => onChange({ search: "" }) }, /* @__PURE__ */ import_react2.default.createElement(import_Clear.default, { fontSize: "small" }))) : void 0
268
+ }
269
+ }
270
+ ), /* @__PURE__ */ import_react2.default.createElement(
271
+ import_material.TextField,
272
+ {
273
+ select: true,
274
+ size: "small",
275
+ label: "Runtime",
276
+ value: filters.runtime,
277
+ onChange: (e) => onChange({ runtime: e.target.value }),
278
+ sx: { minWidth: 140 },
279
+ ...SELECT_PROPS
280
+ },
281
+ runtimes.map((r) => /* @__PURE__ */ import_react2.default.createElement(import_material.MenuItem, { key: r, value: r }, r))
282
+ ), /* @__PURE__ */ import_react2.default.createElement(
283
+ import_material.TextField,
284
+ {
285
+ select: true,
286
+ size: "small",
287
+ label: "Capability",
288
+ value: filters.capability,
289
+ onChange: (e) => onChange({ capability: e.target.value }),
290
+ sx: { minWidth: 150 },
291
+ ...SELECT_PROPS
292
+ },
293
+ capabilities.map((c) => /* @__PURE__ */ import_react2.default.createElement(import_material.MenuItem, { key: c, value: c }, c))
294
+ ), /* @__PURE__ */ import_react2.default.createElement(
295
+ import_material.TextField,
296
+ {
297
+ select: true,
298
+ size: "small",
299
+ label: "Lifecycle",
300
+ value: filters.lifecycle,
301
+ onChange: (e) => onChange({ lifecycle: e.target.value }),
302
+ sx: { minWidth: 130 },
303
+ ...SELECT_PROPS
304
+ },
305
+ lifecycles.map((l) => /* @__PURE__ */ import_react2.default.createElement(import_material.MenuItem, { key: l, value: l }, l))
306
+ ), /* @__PURE__ */ import_react2.default.createElement(
307
+ import_material.TextField,
308
+ {
309
+ select: true,
310
+ size: "small",
311
+ label: "Owner",
312
+ value: filters.owner,
313
+ onChange: (e) => onChange({ owner: e.target.value }),
314
+ sx: { minWidth: 140 },
315
+ ...SELECT_PROPS
316
+ },
317
+ owners.map((o) => /* @__PURE__ */ import_react2.default.createElement(import_material.MenuItem, { key: o, value: o }, o))
318
+ ), hasFilters && /* @__PURE__ */ import_react2.default.createElement(import_material.Tooltip, { title: "Clear filters" }, /* @__PURE__ */ import_react2.default.createElement(import_material.IconButton, { size: "small", onClick: onReset }, /* @__PURE__ */ import_react2.default.createElement(import_Clear.default, { fontSize: "small" }))), /* @__PURE__ */ import_react2.default.createElement(import_material.Box, { sx: { ml: "auto", alignSelf: "center" } }, /* @__PURE__ */ import_react2.default.createElement("strong", null, agents.length), " agent", agents.length !== 1 ? "s" : ""));
319
+ };
320
+ }
321
+ });
322
+
323
+ // src/components/AgentAvatar.tsx
324
+ function initialsOf(name) {
325
+ const parts = name.replace(/[-_]+/g, " ").trim().split(/\s+/);
326
+ if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
327
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
328
+ }
329
+ function colorFor(name) {
330
+ let hash = 0;
331
+ for (let i = 0; i < name.length; i++) hash = hash * 31 + name.charCodeAt(i) | 0;
332
+ return PALETTE[Math.abs(hash) % PALETTE.length];
333
+ }
334
+ var import_react3, import_material2, PALETTE, AgentAvatar;
335
+ var init_AgentAvatar = __esm({
336
+ "src/components/AgentAvatar.tsx"() {
337
+ "use strict";
338
+ import_react3 = __toESM(require("react"));
339
+ import_material2 = require("@mui/material");
340
+ PALETTE = [
341
+ "#1976d2",
342
+ "#388e3c",
343
+ "#f57c00",
344
+ "#7b1fa2",
345
+ "#c62828",
346
+ "#0097a7",
347
+ "#5d4037",
348
+ "#455a64"
349
+ ];
350
+ AgentAvatar = ({
351
+ name,
352
+ avatarUrl,
353
+ size = 44
354
+ }) => {
355
+ const [broken, setBroken] = (0, import_react3.useState)(false);
356
+ const showImage = avatarUrl && !broken;
357
+ return /* @__PURE__ */ import_react3.default.createElement(
358
+ import_material2.Box,
359
+ {
360
+ sx: {
361
+ width: size,
362
+ height: size,
363
+ flexShrink: 0,
364
+ borderRadius: "50%",
365
+ overflow: "hidden",
366
+ display: "flex",
367
+ alignItems: "center",
368
+ justifyContent: "center",
369
+ bgcolor: showImage ? "transparent" : colorFor(name),
370
+ color: "common.white",
371
+ fontSize: size * 0.36,
372
+ fontWeight: 700
373
+ }
374
+ },
375
+ showImage ? /* @__PURE__ */ import_react3.default.createElement(
376
+ "img",
377
+ {
378
+ src: avatarUrl,
379
+ alt: name,
380
+ width: size,
381
+ height: size,
382
+ onError: () => setBroken(true),
383
+ style: { objectFit: "cover", borderRadius: "50%" }
384
+ }
385
+ ) : initialsOf(name)
386
+ );
387
+ };
388
+ }
389
+ });
390
+
391
+ // src/components/AgentStatusBadge.tsx
392
+ var import_react4, import_material3, STATE_COLOR, AgentStatusBadge;
393
+ var init_AgentStatusBadge = __esm({
394
+ "src/components/AgentStatusBadge.tsx"() {
395
+ "use strict";
396
+ import_react4 = __toESM(require("react"));
397
+ import_material3 = require("@mui/material");
398
+ STATE_COLOR = {
399
+ healthy: "success.main",
400
+ degraded: "warning.main",
401
+ down: "error.main",
402
+ unknown: "text.disabled"
403
+ };
404
+ AgentStatusBadge = ({ status }) => {
405
+ const state = status?.state ?? "unknown";
406
+ const color = STATE_COLOR[state];
407
+ const ring = state === "unknown" ? `1px dashed ${color}` : "none";
408
+ const title = status ? [
409
+ `Status: ${state}`,
410
+ status.lastChecked ? `Last checked: ${new Date(status.lastChecked).toLocaleString()}` : null,
411
+ status.latencyMs != null ? `Latency: ${status.latencyMs}ms` : null,
412
+ status.message ? status.message : null
413
+ ].filter(Boolean).join(" \xB7 ") : "Status: unknown";
414
+ return /* @__PURE__ */ import_react4.default.createElement(import_material3.Tooltip, { title, arrow: true }, /* @__PURE__ */ import_react4.default.createElement(
415
+ import_material3.Box,
416
+ {
417
+ sx: {
418
+ display: "inline-flex",
419
+ alignItems: "center",
420
+ gap: 0.75,
421
+ cursor: "help"
422
+ }
423
+ },
424
+ /* @__PURE__ */ import_react4.default.createElement(
425
+ import_material3.Box,
426
+ {
427
+ component: "span",
428
+ sx: {
429
+ width: 10,
430
+ height: 10,
431
+ borderRadius: "50%",
432
+ bgcolor: color,
433
+ border: ring,
434
+ flexShrink: 0
435
+ }
436
+ }
437
+ ),
438
+ status?.latencyMs != null && /* @__PURE__ */ import_react4.default.createElement(import_material3.Typography, { variant: "caption", color: "text.secondary" }, status.latencyMs, "ms")
439
+ ));
440
+ };
441
+ }
442
+ });
443
+
444
+ // src/components/AgentCapabilities.tsx
445
+ var import_react5, import_material4, CATEGORY_COLOR, MAX_VISIBLE, AgentCapabilities;
446
+ var init_AgentCapabilities = __esm({
447
+ "src/components/AgentCapabilities.tsx"() {
448
+ "use strict";
449
+ import_react5 = __toESM(require("react"));
450
+ import_material4 = require("@mui/material");
451
+ CATEGORY_COLOR = {
452
+ reasoning: "primary",
453
+ retrieval: "info",
454
+ tools: "secondary",
455
+ vision: "success",
456
+ voice: "warning",
457
+ data: "default",
458
+ safety: "error"
459
+ };
460
+ MAX_VISIBLE = 5;
461
+ AgentCapabilities = ({
462
+ capabilities,
463
+ max = MAX_VISIBLE,
464
+ size = "small"
465
+ }) => {
466
+ const [anchor, setAnchor] = (0, import_react5.useState)(null);
467
+ if (!capabilities.length) return null;
468
+ const visible = capabilities.slice(0, max);
469
+ const overflow = capabilities.length - visible.length;
470
+ return /* @__PURE__ */ import_react5.default.createElement(import_material4.Box, { sx: { display: "flex", flexWrap: "wrap", gap: 0.5 } }, visible.map((c, i) => /* @__PURE__ */ import_react5.default.createElement(
471
+ import_material4.Chip,
472
+ {
473
+ key: `${c.label}-${i}`,
474
+ label: c.label,
475
+ size,
476
+ color: c.category ? CATEGORY_COLOR[c.category] : "default",
477
+ variant: c.category ? "filled" : "outlined"
478
+ }
479
+ )), overflow > 0 && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(
480
+ import_material4.Chip,
481
+ {
482
+ label: `+${overflow}`,
483
+ size,
484
+ clickable: true,
485
+ onClick: (e) => {
486
+ e.stopPropagation();
487
+ setAnchor(e.currentTarget);
488
+ }
489
+ }
490
+ ), /* @__PURE__ */ import_react5.default.createElement(
491
+ import_material4.Popover,
492
+ {
493
+ open: Boolean(anchor),
494
+ anchorEl: anchor,
495
+ onClose: () => setAnchor(null),
496
+ anchorOrigin: { vertical: "bottom", horizontal: "left" }
497
+ },
498
+ /* @__PURE__ */ import_react5.default.createElement(import_material4.Box, { sx: { p: 1, maxWidth: 280, display: "flex", flexWrap: "wrap", gap: 0.5 } }, capabilities.map((c, i) => /* @__PURE__ */ import_react5.default.createElement(
499
+ import_material4.Chip,
500
+ {
501
+ key: `${c.label}-${i}`,
502
+ label: c.label,
503
+ size: "small",
504
+ color: c.category ? CATEGORY_COLOR[c.category] : "default",
505
+ variant: c.category ? "filled" : "outlined"
506
+ }
507
+ )))
508
+ )));
509
+ };
510
+ }
511
+ });
512
+
513
+ // src/components/RuntimeBadge.tsx
514
+ var import_react6, import_material5, import_Memory, import_CloudQueue, import_Functions, import_Extension, RUNTIME_META, RuntimeBadge;
515
+ var init_RuntimeBadge = __esm({
516
+ "src/components/RuntimeBadge.tsx"() {
517
+ "use strict";
518
+ import_react6 = __toESM(require("react"));
519
+ import_material5 = require("@mui/material");
520
+ import_Memory = __toESM(require("@mui/icons-material/Memory"));
521
+ import_CloudQueue = __toESM(require("@mui/icons-material/CloudQueue"));
522
+ import_Functions = __toESM(require("@mui/icons-material/Functions"));
523
+ import_Extension = __toESM(require("@mui/icons-material/Extension"));
524
+ RUNTIME_META = {
525
+ "bedrock-agentcore": { label: "Bedrock AgentCore", icon: /* @__PURE__ */ import_react6.default.createElement(import_CloudQueue.default, { fontSize: "small" }) },
526
+ litellm: { label: "LiteLLM", icon: /* @__PURE__ */ import_react6.default.createElement(import_Memory.default, { fontSize: "small" }) },
527
+ lambda: { label: "AWS Lambda", icon: /* @__PURE__ */ import_react6.default.createElement(import_Functions.default, { fontSize: "small" }) },
528
+ custom: { label: "Custom", icon: /* @__PURE__ */ import_react6.default.createElement(import_Extension.default, { fontSize: "small" }) }
529
+ };
530
+ RuntimeBadge = ({
531
+ runtime,
532
+ size = "small",
533
+ onClick
534
+ }) => {
535
+ const meta = RUNTIME_META[runtime] ?? {
536
+ label: String(runtime),
537
+ icon: /* @__PURE__ */ import_react6.default.createElement(import_Extension.default, { fontSize: "small" })
538
+ };
539
+ return /* @__PURE__ */ import_react6.default.createElement(
540
+ import_material5.Chip,
541
+ {
542
+ size,
543
+ variant: "outlined",
544
+ label: /* @__PURE__ */ import_react6.default.createElement(import_material5.Box, { sx: { display: "inline-flex", alignItems: "center", gap: 0.5 } }, meta.icon, meta.label),
545
+ onClick: onClick ? () => onClick(runtime) : void 0,
546
+ clickable: Boolean(onClick)
547
+ }
548
+ );
549
+ };
550
+ }
551
+ });
552
+
553
+ // src/components/BillingBadge.tsx
554
+ var import_react7, import_material6, BILLING_COLOR, BillingBadge;
555
+ var init_BillingBadge = __esm({
556
+ "src/components/BillingBadge.tsx"() {
557
+ "use strict";
558
+ import_react7 = __toESM(require("react"));
559
+ import_material6 = require("@mui/material");
560
+ BILLING_COLOR = {
561
+ "per-invocation": "primary",
562
+ "per-token": "secondary",
563
+ subscription: "success",
564
+ free: "default"
565
+ };
566
+ BillingBadge = ({ billing }) => {
567
+ const color = BILLING_COLOR[billing.model] ?? "default";
568
+ const unitLabel = billing.model === "per-token" ? "per 1M tokens" : billing.model === "per-invocation" ? "per 1k calls" : null;
569
+ return /* @__PURE__ */ import_react7.default.createElement(import_material6.Box, { sx: { display: "flex", flexDirection: "column", gap: 0.25 } }, /* @__PURE__ */ import_react7.default.createElement(import_material6.Chip, { size: "small", color, label: billing.model, variant: "outlined" }), billing.unitCost != null && unitLabel && /* @__PURE__ */ import_react7.default.createElement(import_material6.Typography, { variant: "caption", color: "text.secondary" }, "~$", billing.unitCost, " ", unitLabel), billing.budget != null && /* @__PURE__ */ import_react7.default.createElement(import_material6.Typography, { variant: "caption", color: "text.secondary" }, "budget: $", billing.budget));
570
+ };
571
+ }
572
+ });
573
+
574
+ // src/components/AgentCard.tsx
575
+ var import_react8, import_material7, import_Dashboard, import_Description, import_Article, import_BugReport, LIFECYCLE_COLOR, LINK_ICON, AgentCard;
576
+ var init_AgentCard = __esm({
577
+ "src/components/AgentCard.tsx"() {
578
+ "use strict";
579
+ import_react8 = __toESM(require("react"));
580
+ import_material7 = require("@mui/material");
581
+ import_Dashboard = __toESM(require("@mui/icons-material/Dashboard"));
582
+ import_Description = __toESM(require("@mui/icons-material/Description"));
583
+ import_Article = __toESM(require("@mui/icons-material/Article"));
584
+ import_BugReport = __toESM(require("@mui/icons-material/BugReport"));
585
+ init_AgentAvatar();
586
+ init_AgentStatusBadge();
587
+ init_AgentCapabilities();
588
+ init_RuntimeBadge();
589
+ init_BillingBadge();
590
+ LIFECYCLE_COLOR = {
591
+ production: "success",
592
+ experimental: "warning",
593
+ deprecated: "error"
594
+ };
595
+ LINK_ICON = {
596
+ dashboard: /* @__PURE__ */ import_react8.default.createElement(import_Dashboard.default, { fontSize: "small" }),
597
+ docs: /* @__PURE__ */ import_react8.default.createElement(import_Description.default, { fontSize: "small" }),
598
+ playbook: /* @__PURE__ */ import_react8.default.createElement(import_Article.default, { fontSize: "small" }),
599
+ issues: /* @__PURE__ */ import_react8.default.createElement(import_BugReport.default, { fontSize: "small" })
600
+ };
601
+ AgentCard = ({
602
+ agent,
603
+ onClick,
604
+ onRuntimeClick
605
+ }) => {
606
+ const title = agent.title ?? agent.name;
607
+ const lifecycleColor = agent.lifecycle ? LIFECYCLE_COLOR[agent.lifecycle] ?? "default" : "default";
608
+ return /* @__PURE__ */ import_react8.default.createElement(
609
+ import_material7.Card,
610
+ {
611
+ variant: "outlined",
612
+ sx: {
613
+ height: "100%",
614
+ display: "flex",
615
+ flexDirection: "column",
616
+ transition: (theme) => `box-shadow ${theme.transitions.duration.short}ms`,
617
+ "&:hover": { boxShadow: 6 }
618
+ }
619
+ },
620
+ /* @__PURE__ */ import_react8.default.createElement(
621
+ import_material7.CardActionArea,
622
+ {
623
+ onClick: () => onClick?.(agent),
624
+ sx: { flexGrow: 1, p: 2, alignItems: "stretch", display: "flex" }
625
+ },
626
+ /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { display: "flex", flexDirection: "column", width: "100%" } }, /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { display: "flex", alignItems: "center", gap: 1.5, mb: 1 } }, /* @__PURE__ */ import_react8.default.createElement(AgentAvatar, { name: agent.name, avatarUrl: agent.avatarUrl }), /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { flexGrow: 1, minWidth: 0 } }, /* @__PURE__ */ import_react8.default.createElement(import_material7.Typography, { variant: "subtitle1", fontWeight: 700, noWrap: true, title }, title), /* @__PURE__ */ import_react8.default.createElement(import_material7.Typography, { variant: "caption", color: "text.secondary", noWrap: true }, agent.name)), /* @__PURE__ */ import_react8.default.createElement(AgentStatusBadge, { status: agent.status })), /* @__PURE__ */ import_react8.default.createElement(
627
+ import_material7.Typography,
628
+ {
629
+ variant: "body2",
630
+ color: "text.secondary",
631
+ sx: {
632
+ display: "-webkit-box",
633
+ WebkitLineClamp: 2,
634
+ WebkitBoxOrient: "vertical",
635
+ overflow: "hidden",
636
+ minHeight: "2.6em",
637
+ mb: 1.5
638
+ }
639
+ },
640
+ agent.purpose || "No description provided."
641
+ ), /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { mb: 1 } }, /* @__PURE__ */ import_react8.default.createElement(
642
+ RuntimeBadge,
643
+ {
644
+ runtime: agent.runtime.runtime,
645
+ onClick: onRuntimeClick
646
+ }
647
+ )), agent.capabilities.length > 0 && /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(AgentCapabilities, { capabilities: agent.capabilities })), /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { mb: 1.5 } }, /* @__PURE__ */ import_react8.default.createElement(BillingBadge, { billing: agent.billing })), /* @__PURE__ */ import_react8.default.createElement(
648
+ import_material7.Box,
649
+ {
650
+ sx: {
651
+ mt: "auto",
652
+ display: "flex",
653
+ alignItems: "center",
654
+ gap: 0.75,
655
+ flexWrap: "wrap"
656
+ }
657
+ },
658
+ agent.owner && /* @__PURE__ */ import_react8.default.createElement(import_material7.Typography, { variant: "caption", color: "text.secondary" }, agent.owner),
659
+ agent.lifecycle && /* @__PURE__ */ import_react8.default.createElement(
660
+ import_material7.Chip,
661
+ {
662
+ size: "small",
663
+ color: lifecycleColor,
664
+ label: agent.lifecycle,
665
+ variant: "outlined",
666
+ sx: { height: 20, "& .MuiChip-label": { px: 0.75, fontSize: "0.7rem" } }
667
+ }
668
+ ),
669
+ agent.version && /* @__PURE__ */ import_react8.default.createElement(import_material7.Typography, { variant: "caption", color: "text.secondary" }, "v", agent.version)
670
+ ), agent.links.length > 0 && /* @__PURE__ */ import_react8.default.createElement(import_material7.Box, { sx: { display: "flex", gap: 1.5, mt: 1.5, pt: 1, borderTop: "1px solid", borderColor: "divider" } }, agent.links.slice(0, 4).map((l, i) => /* @__PURE__ */ import_react8.default.createElement(import_material7.Tooltip, { key: i, title: l.title }, /* @__PURE__ */ import_react8.default.createElement(
671
+ import_material7.Link,
672
+ {
673
+ href: l.url,
674
+ target: "_blank",
675
+ rel: "noopener noreferrer",
676
+ onClick: (e) => e.stopPropagation(),
677
+ sx: { display: "inline-flex", color: "text.secondary" }
678
+ },
679
+ LINK_ICON[l.icon ?? ""] ?? /* @__PURE__ */ import_react8.default.createElement(import_Description.default, { fontSize: "small" })
680
+ )))))
681
+ )
682
+ );
683
+ };
684
+ }
685
+ });
686
+
687
+ // src/components/AgentsGrid.tsx
688
+ var import_react9, import_Box, AgentsGrid;
689
+ var init_AgentsGrid = __esm({
690
+ "src/components/AgentsGrid.tsx"() {
691
+ "use strict";
692
+ import_react9 = __toESM(require("react"));
693
+ import_Box = __toESM(require("@mui/material/Box"));
694
+ init_AgentCard();
695
+ AgentsGrid = ({
696
+ agents,
697
+ onAgentClick,
698
+ onRuntimeClick
699
+ }) => {
700
+ return /* @__PURE__ */ import_react9.default.createElement(
701
+ import_Box.default,
702
+ {
703
+ sx: {
704
+ display: "grid",
705
+ gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))",
706
+ gap: 2
707
+ }
708
+ },
709
+ agents.map((a) => /* @__PURE__ */ import_react9.default.createElement(
710
+ AgentCard,
711
+ {
712
+ key: a.entityRef,
713
+ agent: a,
714
+ onClick: onAgentClick,
715
+ onRuntimeClick
716
+ }
717
+ ))
718
+ );
719
+ };
720
+ }
721
+ });
722
+
723
+ // src/components/AgentDetailDrawer.tsx
724
+ var import_react10, import_material8, import_Close, import_OpenInNew, import_Refresh, import_Dashboard2, import_Description2, import_Article2, import_BugReport2, import_ContentCopy, LINK_ICON2, Row, AgentDetailDrawer;
725
+ var init_AgentDetailDrawer = __esm({
726
+ "src/components/AgentDetailDrawer.tsx"() {
727
+ "use strict";
728
+ import_react10 = __toESM(require("react"));
729
+ import_material8 = require("@mui/material");
730
+ import_Close = __toESM(require("@mui/icons-material/Close"));
731
+ import_OpenInNew = __toESM(require("@mui/icons-material/OpenInNew"));
732
+ import_Refresh = __toESM(require("@mui/icons-material/Refresh"));
733
+ import_Dashboard2 = __toESM(require("@mui/icons-material/Dashboard"));
734
+ import_Description2 = __toESM(require("@mui/icons-material/Description"));
735
+ import_Article2 = __toESM(require("@mui/icons-material/Article"));
736
+ import_BugReport2 = __toESM(require("@mui/icons-material/BugReport"));
737
+ import_ContentCopy = __toESM(require("@mui/icons-material/ContentCopy"));
738
+ init_AgentAvatar();
739
+ init_AgentStatusBadge();
740
+ init_AgentCapabilities();
741
+ init_RuntimeBadge();
742
+ init_BillingBadge();
743
+ LINK_ICON2 = {
744
+ dashboard: /* @__PURE__ */ import_react10.default.createElement(import_Dashboard2.default, { fontSize: "small" }),
745
+ docs: /* @__PURE__ */ import_react10.default.createElement(import_Description2.default, { fontSize: "small" }),
746
+ playbook: /* @__PURE__ */ import_react10.default.createElement(import_Article2.default, { fontSize: "small" }),
747
+ issues: /* @__PURE__ */ import_react10.default.createElement(import_BugReport2.default, { fontSize: "small" })
748
+ };
749
+ Row = ({
750
+ label,
751
+ children
752
+ }) => /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { display: "flex", gap: 1, mb: 1 } }, /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "body2", color: "text.secondary", sx: { minWidth: 110 } }, label), /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { flexGrow: 1 } }, children));
753
+ AgentDetailDrawer = ({
754
+ agent,
755
+ open,
756
+ onClose,
757
+ onRefreshStatus
758
+ }) => {
759
+ if (!agent) return null;
760
+ const title = agent.title ?? agent.name;
761
+ const copyRef = () => {
762
+ navigator.clipboard?.writeText(agent.entityRef).catch(() => {
763
+ });
764
+ };
765
+ return /* @__PURE__ */ import_react10.default.createElement(
766
+ import_material8.Drawer,
767
+ {
768
+ anchor: "right",
769
+ open,
770
+ onClose,
771
+ sx: { "& .MuiDrawer-paper": { width: { xs: "100%", sm: 480 } } }
772
+ },
773
+ /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { p: 2, display: "flex", alignItems: "flex-start", gap: 1.5 } }, /* @__PURE__ */ import_react10.default.createElement(AgentAvatar, { name: agent.name, avatarUrl: agent.avatarUrl, size: 56 }), /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { flexGrow: 1 } }, /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "h6" }, title), /* @__PURE__ */ import_react10.default.createElement(
774
+ import_material8.Typography,
775
+ {
776
+ variant: "caption",
777
+ color: "text.secondary",
778
+ sx: {
779
+ display: "inline-flex",
780
+ alignItems: "center",
781
+ gap: 0.5,
782
+ cursor: "pointer"
783
+ },
784
+ onClick: copyRef
785
+ },
786
+ agent.entityRef,
787
+ /* @__PURE__ */ import_react10.default.createElement(import_ContentCopy.default, { sx: { fontSize: 12 } })
788
+ )), /* @__PURE__ */ import_react10.default.createElement(import_material8.IconButton, { size: "small", onClick: onClose }, /* @__PURE__ */ import_react10.default.createElement(import_Close.default, { fontSize: "small" }))),
789
+ /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { px: 2, pb: 2, display: "flex", alignItems: "center", gap: 1 } }, /* @__PURE__ */ import_react10.default.createElement(AgentStatusBadge, { status: agent.status }), onRefreshStatus && /* @__PURE__ */ import_react10.default.createElement(
790
+ import_material8.IconButton,
791
+ {
792
+ size: "small",
793
+ title: "Refresh status",
794
+ onClick: () => onRefreshStatus(agent.entityRef)
795
+ },
796
+ /* @__PURE__ */ import_react10.default.createElement(import_Refresh.default, { fontSize: "small" })
797
+ )),
798
+ /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { px: 2, pb: 3 } }, /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "body2", sx: { mb: 2 } }, agent.purpose || agent.description || "No description provided."), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Runtime" }, /* @__PURE__ */ import_react10.default.createElement(import_material8.Stack, { direction: "row", spacing: 1, alignItems: "center" }, /* @__PURE__ */ import_react10.default.createElement(RuntimeBadge, { runtime: agent.runtime.runtime }), agent.runtime.endpoint && /* @__PURE__ */ import_react10.default.createElement(
799
+ import_material8.Link,
800
+ {
801
+ href: agent.runtime.endpoint,
802
+ target: "_blank",
803
+ rel: "noopener noreferrer",
804
+ sx: { display: "inline-flex", alignItems: "center", gap: 0.5 }
805
+ },
806
+ "endpoint ",
807
+ /* @__PURE__ */ import_react10.default.createElement(import_OpenInNew.default, { sx: { fontSize: 12 } })
808
+ )), agent.runtime.runtimeHandle && /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "caption", display: "block", color: "text.secondary", sx: { mt: 0.5, wordBreak: "break-all" } }, agent.runtime.runtimeHandle)), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Capabilities" }, agent.capabilities.length ? /* @__PURE__ */ import_react10.default.createElement(AgentCapabilities, { capabilities: agent.capabilities, max: 20 }) : /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "body2", color: "text.secondary" }, "\u2014")), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Billing" }, /* @__PURE__ */ import_react10.default.createElement(BillingBadge, { billing: agent.billing })), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Owner" }, agent.owner ?? "\u2014"), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "System" }, agent.system ?? "\u2014"), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Lifecycle" }, agent.lifecycle ?? "\u2014"), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Version" }, agent.version ?? "\u2014"), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Tags" }, agent.tags.length ? /* @__PURE__ */ import_react10.default.createElement(import_material8.Box, { sx: { display: "flex", flexWrap: "wrap", gap: 0.5 } }, agent.tags.map((t) => /* @__PURE__ */ import_react10.default.createElement(import_material8.Chip, { key: t, size: "small", label: t, variant: "outlined" }))) : /* @__PURE__ */ import_react10.default.createElement(import_material8.Typography, { variant: "body2", color: "text.secondary" }, "\u2014")), agent.links.length > 0 && /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Links" }, /* @__PURE__ */ import_react10.default.createElement(import_material8.List, { dense: true, disablePadding: true }, agent.links.map((l, i) => /* @__PURE__ */ import_react10.default.createElement(
809
+ import_material8.ListItem,
810
+ {
811
+ key: i,
812
+ component: "a",
813
+ href: l.url,
814
+ target: "_blank",
815
+ rel: "noopener noreferrer",
816
+ sx: { color: "text.primary", borderRadius: 1, "&:hover": { bgcolor: "action.hover" } }
817
+ },
818
+ /* @__PURE__ */ import_react10.default.createElement(import_material8.ListItemIcon, { sx: { minWidth: 28 } }, LINK_ICON2[l.icon ?? ""] ?? /* @__PURE__ */ import_react10.default.createElement(import_Description2.default, { fontSize: "small" })),
819
+ /* @__PURE__ */ import_react10.default.createElement(
820
+ import_material8.ListItemText,
821
+ {
822
+ primary: l.title,
823
+ secondary: l.url,
824
+ secondaryTypographyProps: { sx: { fontSize: "0.7rem" }, noWrap: true }
825
+ }
826
+ )
827
+ )))), /* @__PURE__ */ import_react10.default.createElement(Row, { label: "Catalog" }, /* @__PURE__ */ import_react10.default.createElement(import_material8.Link, { href: `/catalog/${agent.entityRef.replace(":", "/")}` }, "Open in catalog ", /* @__PURE__ */ import_react10.default.createElement(import_OpenInNew.default, { sx: { fontSize: 12, verticalAlign: "middle" } }))))
828
+ );
829
+ };
830
+ }
831
+ });
832
+
833
+ // src/components/AgentsPage.tsx
834
+ var AgentsPage_exports = {};
835
+ __export(AgentsPage_exports, {
836
+ AgentsPage: () => AgentsPage
837
+ });
838
+ var import_react11, import_material9, import_core_components, import_core_plugin_api3, POLL_INTERVAL_MS, AgentsPage;
839
+ var init_AgentsPage = __esm({
840
+ "src/components/AgentsPage.tsx"() {
841
+ "use strict";
842
+ import_react11 = __toESM(require("react"));
843
+ import_material9 = require("@mui/material");
844
+ import_core_components = require("@backstage/core-components");
845
+ import_core_plugin_api3 = require("@backstage/core-plugin-api");
846
+ init_api();
847
+ init_useAgents();
848
+ init_AgentFilters();
849
+ init_AgentsGrid();
850
+ init_AgentDetailDrawer();
851
+ POLL_INTERVAL_MS = 3e4;
852
+ AgentsPage = () => {
853
+ const api = (0, import_core_plugin_api3.useApi)(aiAgentsApiRef);
854
+ const { agents, allAgents, loading, error, retry, filters, update, reset } = useAgents();
855
+ const [selected, setSelected] = (0, import_react11.useState)(null);
856
+ const [drawerOpen, setDrawerOpen] = (0, import_react11.useState)(false);
857
+ const [statuses, setStatuses] = (0, import_react11.useState)({});
858
+ const refs = allAgents.map((a) => a.entityRef);
859
+ (0, import_react11.useEffect)(() => {
860
+ let cancelled = false;
861
+ if (!refs.length) return;
862
+ api.getStatuses(refs).then((result) => {
863
+ if (!cancelled) setStatuses(result);
864
+ });
865
+ return () => {
866
+ cancelled = true;
867
+ };
868
+ }, [api, refs.join(",")]);
869
+ (0, import_react11.useEffect)(() => {
870
+ if (!refs.length) return;
871
+ const id = setInterval(async () => {
872
+ const result = await api.getStatuses(refs);
873
+ setStatuses((prev) => ({ ...prev, ...result }));
874
+ }, POLL_INTERVAL_MS);
875
+ return () => clearInterval(id);
876
+ }, [api, refs.length]);
877
+ const agentsWithStatus = agents.map((a) => ({
878
+ ...a,
879
+ status: statuses[a.entityRef] ?? a.status
880
+ }));
881
+ const handleCardClick = (0, import_react11.useCallback)((agent) => {
882
+ setSelected(agent);
883
+ setDrawerOpen(true);
884
+ }, []);
885
+ const handleRuntimeClick = (0, import_react11.useCallback)(
886
+ (runtime) => {
887
+ if (!filters.runtime.includes(runtime)) {
888
+ update({ runtime: [...filters.runtime, runtime] });
889
+ }
890
+ },
891
+ [filters.runtime, update]
892
+ );
893
+ const handleRefreshStatus = (0, import_react11.useCallback)(
894
+ async (entityRef2) => {
895
+ const result = await api.getStatuses([entityRef2]);
896
+ setStatuses((prev) => ({ ...prev, ...result }));
897
+ setSelected(
898
+ (prev) => prev && prev.entityRef === entityRef2 ? { ...prev, status: result[entityRef2] ?? prev.status } : prev
899
+ );
900
+ },
901
+ [api]
902
+ );
903
+ if (loading && !allAgents.length) {
904
+ return /* @__PURE__ */ import_react11.default.createElement(import_material9.Box, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "40vh" }, /* @__PURE__ */ import_react11.default.createElement(import_material9.CircularProgress, null));
905
+ }
906
+ if (error) {
907
+ return /* @__PURE__ */ import_react11.default.createElement(
908
+ import_core_components.EmptyState,
909
+ {
910
+ title: "Failed to load AI agents",
911
+ description: error.message,
912
+ missing: "content"
913
+ }
914
+ );
915
+ }
916
+ return /* @__PURE__ */ import_react11.default.createElement(import_material9.Box, { sx: { p: 3 } }, /* @__PURE__ */ import_react11.default.createElement(import_material9.Box, { sx: { mb: 2 } }, /* @__PURE__ */ import_react11.default.createElement(import_material9.Typography, { variant: "h4" }, "AI Agents"), /* @__PURE__ */ import_react11.default.createElement(import_material9.Typography, { variant: "body2", color: "text.secondary" }, "AI agents registered in the catalog as ", /* @__PURE__ */ import_react11.default.createElement("code", null, "Component"), " entities with ", /* @__PURE__ */ import_react11.default.createElement("code", null, "spec.type: ai-agent"), ".")), /* @__PURE__ */ import_react11.default.createElement(
917
+ AgentFiltersBar,
918
+ {
919
+ agents: allAgents,
920
+ filters,
921
+ onChange: update,
922
+ onReset: reset
923
+ }
924
+ ), agentsWithStatus.length === 0 ? /* @__PURE__ */ import_react11.default.createElement(
925
+ import_core_components.EmptyState,
926
+ {
927
+ title: "No AI agents registered",
928
+ description: "Add a Component with spec.type: ai-agent to the catalog, or adjust your filters.",
929
+ missing: "content",
930
+ action: /* @__PURE__ */ import_react11.default.createElement("button", { onClick: () => retry() }, "Retry")
931
+ }
932
+ ) : /* @__PURE__ */ import_react11.default.createElement(
933
+ AgentsGrid,
934
+ {
935
+ agents: agentsWithStatus,
936
+ onAgentClick: handleCardClick,
937
+ onRuntimeClick: handleRuntimeClick
938
+ }
939
+ ), /* @__PURE__ */ import_react11.default.createElement(
940
+ AgentDetailDrawer,
941
+ {
942
+ agent: selected,
943
+ open: drawerOpen,
944
+ onClose: () => setDrawerOpen(false),
945
+ onRefreshStatus: handleRefreshStatus
946
+ }
947
+ ));
948
+ };
949
+ }
950
+ });
951
+
952
+ // src/index.ts
953
+ var index_exports = {};
954
+ __export(index_exports, {
955
+ AI_AGENT_ANNOTATION_PREFIX: () => AI_AGENT_ANNOTATION_PREFIX,
956
+ AI_AGENT_TYPE: () => AI_AGENT_TYPE,
957
+ AgentCard: () => AgentCard,
958
+ AgentsPage: () => AgentsPage,
959
+ AiAgentsApi: () => AiAgentsApi,
960
+ aiAgentsApiRef: () => aiAgentsApiRef,
961
+ aiAgentsPlugin: () => aiAgentsPlugin,
962
+ entityToAgent: () => entityToAgent
963
+ });
964
+ module.exports = __toCommonJS(index_exports);
965
+
966
+ // src/plugin.tsx
967
+ var import_react12 = __toESM(require("react"));
968
+ var import_icons_material = require("@mui/icons-material");
969
+ var import_frontend_plugin_api = require("@backstage/frontend-plugin-api");
970
+ var import_plugin_catalog_react = require("@backstage/plugin-catalog-react");
971
+ init_api();
972
+ var aiAgentsApi = import_frontend_plugin_api.ApiBlueprint.make({
973
+ params: (defineParams) => defineParams({
974
+ api: aiAgentsApiRef,
975
+ deps: { fetchApi: import_frontend_plugin_api.fetchApiRef, catalogApi: import_plugin_catalog_react.catalogApiRef },
976
+ factory: ({ fetchApi, catalogApi }) => new AiAgentsApi({ fetchApi, catalogApi })
977
+ })
978
+ });
979
+ var aiAgentsPage = import_frontend_plugin_api.PageBlueprint.make({
980
+ params: {
981
+ path: "/ai-agents",
982
+ title: "AI Agents",
983
+ icon: /* @__PURE__ */ import_react12.default.createElement(import_icons_material.SmartToy, null),
984
+ loader: async () => {
985
+ const { AgentsPage: AgentsPage2 } = await Promise.resolve().then(() => (init_AgentsPage(), AgentsPage_exports));
986
+ return /* @__PURE__ */ import_react12.default.createElement(AgentsPage2, null);
987
+ }
988
+ }
989
+ });
990
+ var aiAgentsPlugin = (0, import_frontend_plugin_api.createFrontendPlugin)({
991
+ pluginId: "ai-agents",
992
+ extensions: [aiAgentsApi, aiAgentsPage]
993
+ });
994
+
995
+ // src/index.ts
996
+ init_AgentsPage();
997
+ init_AgentCard();
998
+ init_api();
999
+ init_types();
1000
+ //# sourceMappingURL=index.cjs.js.map