@mcoda/agent-setup 0.1.66 → 0.1.70

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.
package/README.md CHANGED
@@ -11,4 +11,13 @@ The package provides:
11
11
  - an optional React setup page
12
12
 
13
13
  The default server runtime uses mcoda package APIs directly and does not require
14
- a preinstalled or configured `mcoda` CLI/client tool.
14
+ a preinstalled or configured `mcoda` CLI/client tool. When an admin submits a
15
+ real mswarm API key through `configureMswarmApiKey()`, subsequent cloud and
16
+ self-hosted catalog reads use the real mswarm API via `MswarmApi`.
17
+
18
+ React consumers can use the packaged default stylesheet:
19
+
20
+ ```ts
21
+ import { McodaAgentSetupPage } from "@mcoda/agent-setup/react";
22
+ import "@mcoda/agent-setup/react/styles.css";
23
+ ```
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"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"}
@@ -22,9 +22,11 @@ export function McodaAgentSetupPage(props) {
22
22
  const [apiKey, setApiKey] = React.useState("");
23
23
  const [busy, setBusy] = React.useState(false);
24
24
  const [error, setError] = React.useState(null);
25
+ const [statusMessage, setStatusMessage] = React.useState(null);
25
26
  const refresh = React.useCallback(async () => {
26
27
  setBusy(true);
27
28
  setError(null);
29
+ setStatusMessage(null);
28
30
  try {
29
31
  const next = await props.client.fetchSnapshot();
30
32
  setSnapshot(next);
@@ -45,11 +47,13 @@ export function McodaAgentSetupPage(props) {
45
47
  return;
46
48
  setBusy(true);
47
49
  setError(null);
50
+ setStatusMessage(null);
48
51
  try {
49
52
  const next = await props.client.configureMswarmApiKey({ apiKey });
50
53
  setApiKey("");
51
54
  setSnapshot(next);
52
55
  setAssignments(next.assignments);
56
+ setStatusMessage("mswarm API key saved and agent catalogs synced.");
53
57
  }
54
58
  catch (caught) {
55
59
  setError(caught instanceof Error ? caught.message : String(caught));
@@ -61,10 +65,12 @@ export function McodaAgentSetupPage(props) {
61
65
  const syncAgents = async () => {
62
66
  setBusy(true);
63
67
  setError(null);
68
+ setStatusMessage(null);
64
69
  try {
65
70
  const next = await props.client.syncAgents();
66
71
  setSnapshot(next);
67
72
  setAssignments(next.assignments);
73
+ setStatusMessage("Cloud and self-hosted agent catalogs synced.");
68
74
  }
69
75
  catch (caught) {
70
76
  setError(caught instanceof Error ? caught.message : String(caught));
@@ -76,10 +82,12 @@ export function McodaAgentSetupPage(props) {
76
82
  const saveAssignments = async () => {
77
83
  setBusy(true);
78
84
  setError(null);
85
+ setStatusMessage(null);
79
86
  try {
80
87
  const next = await props.client.updateAssignments({ assignments });
81
88
  setSnapshot(next);
82
89
  setAssignments(next.assignments);
90
+ setStatusMessage("Stage assignments saved.");
83
91
  }
84
92
  catch (caught) {
85
93
  setError(caught instanceof Error ? caught.message : String(caught));
@@ -88,11 +96,18 @@ export function McodaAgentSetupPage(props) {
88
96
  setBusy(false);
89
97
  }
90
98
  };
91
- return React.createElement("section", { className: joinClasses("mcoda-agent-setup", props.className) }, React.createElement("header", { className: "mcoda-agent-setup__header" }, React.createElement("h1", null, props.title ?? "mcoda Agent Setup"), React.createElement("button", { type: "button", disabled: busy, onClick: refresh }, "Refresh")), error
92
- ? React.createElement("p", { className: "mcoda-agent-setup__error" }, error)
93
- : null, snapshot
99
+ return React.createElement("section", { className: joinClasses("mcoda-agent-setup", props.className) }, React.createElement("header", { className: "mcoda-agent-setup__header" }, React.createElement("div", { className: "mcoda-agent-setup__title-block" }, React.createElement("p", { className: "mcoda-agent-setup__eyebrow" }, "Runtime routing"), React.createElement("h1", null, props.title ?? "mcoda Agent Setup"), React.createElement("p", null, "Configure mswarm access and assign cloud or self-hosted mcoda agents to each runtime stage.")), React.createElement("button", {
100
+ type: "button",
101
+ className: "mcoda-agent-setup__button mcoda-agent-setup__button--secondary",
102
+ disabled: busy,
103
+ onClick: refresh,
104
+ }, busy ? "Refreshing" : "Refresh")), error
105
+ ? React.createElement("p", { className: "mcoda-agent-setup__alert mcoda-agent-setup__alert--error" }, error)
106
+ : null, statusMessage
107
+ ? React.createElement("p", { className: "mcoda-agent-setup__alert mcoda-agent-setup__alert--success" }, statusMessage)
108
+ : null, React.createElement("div", { className: "mcoda-agent-setup__overview" }, snapshot
94
109
  ? React.createElement(components.AgentCatalogSummary, { snapshot })
95
- : React.createElement("p", null, busy ? "Loading" : "No snapshot loaded"), React.createElement(components.MswarmAccessCard, {
110
+ : React.createElement("div", { className: "mcoda-agent-setup__empty-state" }, busy ? "Loading settings." : "No settings loaded."), React.createElement(components.MswarmAccessCard, {
96
111
  apiKey,
97
112
  busy,
98
113
  labels,
@@ -100,7 +115,7 @@ export function McodaAgentSetupPage(props) {
100
115
  onApiKeyChange: setApiKey,
101
116
  onSaveKey: saveKey,
102
117
  onSyncAgents: syncAgents,
103
- }), snapshot
118
+ })), snapshot
104
119
  ? React.createElement(components.StageAgentAssignments, {
105
120
  stages,
106
121
  snapshot,
@@ -115,27 +130,61 @@ export function McodaAgentSetupPage(props) {
115
130
  export function AgentCatalogSummary(props) {
116
131
  const { snapshot } = props;
117
132
  const warningEntries = Object.entries(snapshot.catalog.errors);
118
- return React.createElement("div", { className: "mcoda-agent-setup__summary" }, React.createElement("div", null, `Provider: ${snapshot.provider}`), React.createElement("div", null, `mswarm Key: ${snapshot.mswarmApiKeyConfigured
119
- ? `configured${snapshot.mswarmApiKeyLast4 ? ` (${snapshot.mswarmApiKeyLast4})` : ""}`
120
- : "missing"}`), React.createElement("div", null, `Synced Agents: ${snapshot.catalog.localAgents.length}`), React.createElement("div", null, `Cloud Agents: ${snapshot.catalog.cloudAgents.length}`), React.createElement("div", null, `Self-hosted Servers: ${snapshot.catalog.selfHostedServers.length}`), warningEntries.length
121
- ? React.createElement("ul", { className: "mcoda-agent-setup__warnings" }, warningEntries.map(([key, value]) => React.createElement("li", { key }, `${key}: ${value}`)))
133
+ return React.createElement("section", { className: "mcoda-agent-setup__summary" }, React.createElement("div", { className: "mcoda-agent-setup__card-heading" }, React.createElement("div", null, React.createElement("h2", null, "Catalog Snapshot"), React.createElement("p", null, `Fetched ${formatTimestamp(snapshot.fetchedAt)}`)), React.createElement("span", {
134
+ className: snapshot.mswarmApiKeyConfigured
135
+ ? "mcoda-agent-setup__badge mcoda-agent-setup__badge--success"
136
+ : "mcoda-agent-setup__badge mcoda-agent-setup__badge--warning",
137
+ }, snapshot.mswarmApiKeyConfigured ? "Configured" : "Missing key")), React.createElement("div", { className: "mcoda-agent-setup__metric-grid" }, React.createElement(MetricCard, {
138
+ label: "Provider",
139
+ value: snapshot.provider,
140
+ detail: "active runtime",
141
+ }), React.createElement(MetricCard, {
142
+ label: "mswarm key",
143
+ value: snapshot.mswarmApiKeyLast4
144
+ ? `****${snapshot.mswarmApiKeyLast4}`
145
+ : snapshot.mswarmApiKeyConfigured
146
+ ? "Configured"
147
+ : "Missing",
148
+ detail: `set ${formatTimestamp(snapshot.mswarmConfiguredAt)}`,
149
+ }), React.createElement(MetricCard, {
150
+ label: "Synced agents",
151
+ value: String(snapshot.catalog.localAgents.length),
152
+ detail: "mcoda registry entries",
153
+ }), React.createElement(MetricCard, {
154
+ label: "Cloud agents",
155
+ value: String(snapshot.catalog.cloudAgents.length),
156
+ detail: "mswarm cloud catalog",
157
+ }), React.createElement(MetricCard, {
158
+ label: "Self-hosted",
159
+ value: String(snapshot.catalog.selfHostedAgents.length),
160
+ detail: `${snapshot.catalog.selfHostedServers.length} servers`,
161
+ })), warningEntries.length
162
+ ? React.createElement("ul", { className: "mcoda-agent-setup__warnings", "aria-label": "Catalog warnings" }, warningEntries.map(([key, value]) => React.createElement("li", { key }, `${key}: ${value}`)))
122
163
  : null);
123
164
  }
124
165
  export function MswarmAccessCard(props) {
125
166
  const labels = { ...DEFAULT_LABELS, ...(props.labels ?? {}) };
126
- return React.createElement("section", { className: "mcoda-agent-setup__access" }, React.createElement("h2", null, "mswarm Access"), React.createElement("input", {
167
+ return React.createElement("section", { className: "mcoda-agent-setup__access" }, React.createElement("div", { className: "mcoda-agent-setup__card-heading" }, React.createElement("div", null, React.createElement("h2", null, "mswarm Access"), React.createElement("p", null, props.snapshot?.mswarmApiKeyConfigured
168
+ ? "Replace the stored key or resync the catalog."
169
+ : "Add a key to load real cloud and self-hosted agents."))), React.createElement("label", { className: "mcoda-agent-setup__field" }, React.createElement("span", null, "API key"), React.createElement("input", {
127
170
  type: "password",
128
171
  value: props.apiKey,
129
172
  autoComplete: "off",
130
173
  onChange: (event) => props.onApiKeyChange(event.target.value),
131
174
  placeholder: props.snapshot?.mswarmApiKeyConfigured
132
175
  ? "Replace mswarm API key"
133
- : "mswarm API key",
134
- }), React.createElement("button", {
176
+ : "sk_prod_mswarm_...",
177
+ })), React.createElement("div", { className: "mcoda-agent-setup__action-row" }, React.createElement("button", {
135
178
  type: "button",
179
+ className: "mcoda-agent-setup__button mcoda-agent-setup__button--primary",
136
180
  disabled: props.busy || !props.apiKey.trim(),
137
181
  onClick: props.onSaveKey,
138
- }, labels.saveKey), React.createElement("button", { type: "button", disabled: props.busy, onClick: props.onSyncAgents }, labels.syncAgents));
182
+ }, labels.saveKey), React.createElement("button", {
183
+ type: "button",
184
+ className: "mcoda-agent-setup__button mcoda-agent-setup__button--secondary",
185
+ disabled: props.busy,
186
+ onClick: props.onSyncAgents,
187
+ }, labels.syncAgents)));
139
188
  }
140
189
  export function StageAgentAssignments(props) {
141
190
  const labels = { ...DEFAULT_LABELS, ...(props.labels ?? {}) };
@@ -161,46 +210,76 @@ export function StageAgentAssignments(props) {
161
210
  nextSources[stage.stageKey] = "cloud";
162
211
  }
163
212
  }
164
- setSources((current) => ({ ...nextSources, ...current }));
165
- setServers((current) => ({ ...nextServers, ...current }));
213
+ setSources((current) => ({ ...current, ...nextSources }));
214
+ setServers((current) => ({ ...current, ...nextServers }));
166
215
  }, [props.assignments, props.snapshot.catalog.cloudAgents, props.snapshot.catalog.selfHostedServers, props.stages]);
167
- return React.createElement("section", { className: "mcoda-agent-setup__assignments" }, React.createElement("h2", null, "Stage Assignments"), React.createElement("div", { className: "mcoda-agent-setup__stage-list" }, props.stages.map((stage) => {
216
+ return React.createElement("section", { className: "mcoda-agent-setup__assignments" }, React.createElement("div", { className: "mcoda-agent-setup__card-heading" }, React.createElement("div", null, React.createElement("h2", null, "Stage Assignments"), React.createElement("p", null, "Select the agent that should run each app workflow stage.")), React.createElement("button", {
217
+ type: "button",
218
+ className: "mcoda-agent-setup__button mcoda-agent-setup__button--success",
219
+ disabled: props.busy,
220
+ onClick: props.onSaveAssignments,
221
+ }, labels.saveAssignments)), React.createElement("div", { className: "mcoda-agent-setup__stage-table-wrap" }, React.createElement("table", { className: "mcoda-agent-setup__stage-table" }, React.createElement("thead", null, React.createElement("tr", null, React.createElement("th", null, "Stage"), React.createElement("th", null, "Source"), React.createElement("th", null, "Self-hosted server"), React.createElement("th", null, "Agent"), React.createElement("th", null, "Status"))), React.createElement("tbody", null, props.stages.map((stage) => {
222
+ const selectedSlug = props.assignments[stage.stageKey] ?? "";
168
223
  const source = sources[stage.stageKey] ??
169
224
  (stage.preferredSource === "self_hosted" ? "self_hosted" : "cloud");
170
225
  const serverId = servers[stage.stageKey];
171
- const server = props.snapshot.catalog.selfHostedServers.find((candidate) => candidate.id === serverId);
226
+ const server = props.snapshot.catalog.selfHostedServers.find((candidate) => candidate.id === serverId) ?? props.snapshot.catalog.selfHostedServers[0];
172
227
  const agents = source === "cloud"
173
228
  ? props.snapshot.catalog.cloudAgents
174
229
  : server?.agents ?? [];
175
- return React.createElement("div", { className: "mcoda-agent-setup__stage-row", key: stage.stageKey }, React.createElement("div", null, React.createElement("strong", null, stage.displayName), stage.description
230
+ const allSelectableAgents = [
231
+ ...props.snapshot.catalog.cloudAgents,
232
+ ...props.snapshot.catalog.selfHostedServers.flatMap((candidate) => candidate.agents),
233
+ ];
234
+ const selectedAgent = allSelectableAgents.find((agent) => agent.slug === selectedSlug);
235
+ const agentSelectValue = agents.some((agent) => agent.slug === selectedSlug)
236
+ ? selectedSlug
237
+ : "";
238
+ const onSourceChange = (value) => {
239
+ setSources((current) => ({ ...current, [stage.stageKey]: value }));
240
+ const nextSlug = value === "cloud"
241
+ ? props.snapshot.catalog.cloudAgents[0]?.slug
242
+ : firstSelfHostedAgentSlug(props.snapshot.catalog.selfHostedServers);
243
+ props.onAssignmentChange(stage.stageKey, nextSlug ?? null);
244
+ };
245
+ const onServerChange = (value) => {
246
+ setServers((current) => ({ ...current, [stage.stageKey]: value }));
247
+ const nextServer = props.snapshot.catalog.selfHostedServers.find((candidate) => candidate.id === value);
248
+ props.onAssignmentChange(stage.stageKey, nextServer?.agents[0]?.slug ?? null);
249
+ };
250
+ return React.createElement("tr", { key: stage.stageKey }, React.createElement("td", { className: "mcoda-agent-setup__stage-cell" }, React.createElement("strong", null, stage.displayName), stage.description
176
251
  ? React.createElement("p", null, stage.description)
177
- : null), React.createElement(sourceSelect, {
252
+ : null, React.createElement("code", null, stage.stageKey)), React.createElement("td", null, React.createElement(sourceSelect, {
178
253
  value: source,
179
254
  labels,
180
- onChange: (value) => setSources((current) => ({ ...current, [stage.stageKey]: value })),
181
- }), source === "self_hosted"
255
+ onChange: onSourceChange,
256
+ })), React.createElement("td", null, source === "self_hosted"
182
257
  ? React.createElement(serverSelect, {
183
258
  servers: props.snapshot.catalog.selfHostedServers,
184
- value: serverId ?? "",
185
- onChange: (value) => setServers((current) => ({ ...current, [stage.stageKey]: value })),
259
+ value: server?.id ?? "",
260
+ onChange: onServerChange,
186
261
  })
187
- : null, React.createElement(agentSelect, {
262
+ : React.createElement("span", { className: "mcoda-agent-setup__muted-pill" }, "Cloud catalog")), React.createElement("td", null, React.createElement(agentSelect, {
188
263
  agents,
189
- value: props.assignments[stage.stageKey] ?? "",
264
+ value: agentSelectValue,
190
265
  onChange: (slug) => props.onAssignmentChange(stage.stageKey, slug || null),
191
- }));
192
- })), React.createElement("button", {
193
- type: "button",
194
- disabled: props.busy,
195
- onClick: props.onSaveAssignments,
196
- }, labels.saveAssignments));
266
+ })), React.createElement("td", null, React.createElement(AgentStatusBadge, {
267
+ agent: selectedAgent,
268
+ slug: selectedSlug,
269
+ })));
270
+ })))));
197
271
  }
198
272
  export function AgentSourceSelect(props) {
199
273
  const labels = { ...DEFAULT_LABELS, ...(props.labels ?? {}) };
200
- return React.createElement("select", {
201
- value: props.value,
202
- onChange: (event) => props.onChange(event.target.value === "self_hosted" ? "self_hosted" : "cloud"),
203
- }, React.createElement("option", { value: "cloud" }, labels.cloud), React.createElement("option", { value: "self_hosted" }, labels.selfHosted));
274
+ return React.createElement("div", { className: "mcoda-agent-setup__source", role: "group", "aria-label": "Agent source" }, React.createElement("button", {
275
+ type: "button",
276
+ "aria-pressed": props.value === "cloud",
277
+ onClick: () => props.onChange("cloud"),
278
+ }, labels.cloud), React.createElement("button", {
279
+ type: "button",
280
+ "aria-pressed": props.value === "self_hosted",
281
+ onClick: () => props.onChange("self_hosted"),
282
+ }, labels.selfHosted));
204
283
  }
205
284
  export function SelfHostedServerSelect(props) {
206
285
  const [query, setQuery] = React.useState("");
@@ -219,16 +298,27 @@ export function SelfHostedServerSelect(props) {
219
298
  })
220
299
  : null, React.createElement("select", {
221
300
  value: props.value,
301
+ disabled: filteredServers.length === 0,
222
302
  onChange: (event) => props.onChange(event.target.value),
223
- }, React.createElement("option", { value: "" }, "Select server"), filteredServers.map((server) => React.createElement("option", { key: server.id, value: server.id }, `${server.label} (${server.agentCount})`))));
303
+ }, React.createElement("option", { value: "" }, filteredServers.length ? "Select server" : "No servers synced"), filteredServers.map((server) => React.createElement("option", { key: server.id, value: server.id }, `${server.label} (${server.agentCount})`))));
224
304
  }
225
305
  export function AgentSearchSelect(props) {
226
306
  const [query, setQuery] = React.useState("");
227
307
  const [scrollTop, setScrollTop] = React.useState(0);
228
308
  const [activeIndex, setActiveIndex] = React.useState(0);
309
+ const [open, setOpen] = React.useState(false);
310
+ const triggerRef = React.useRef(null);
311
+ const panelRef = React.useRef(null);
312
+ const searchRef = React.useRef(null);
313
+ const [panelFrame, setPanelFrame] = React.useState({
314
+ left: 0,
315
+ maxHeight: 420,
316
+ top: 0,
317
+ width: 360,
318
+ });
229
319
  const filtered = React.useMemo(() => filterAgentOptions(props.agents, query), [props.agents, query]);
230
- const rowHeight = props.rowHeight ?? 40;
231
- const viewportHeight = props.viewportHeight ?? 240;
320
+ const rowHeight = props.rowHeight ?? 64;
321
+ const viewportHeight = Math.min(props.viewportHeight ?? 320, panelFrame.maxHeight - 74);
232
322
  const windowed = getVirtualAgentWindow(filtered, {
233
323
  scrollTop,
234
324
  rowHeight,
@@ -237,10 +327,62 @@ export function AgentSearchSelect(props) {
237
327
  const selected = props.agents.find((agent) => agent.slug === props.value);
238
328
  React.useEffect(() => {
239
329
  setActiveIndex(0);
330
+ setScrollTop(0);
240
331
  }, [query, props.agents]);
332
+ React.useEffect(() => {
333
+ if (!open)
334
+ return;
335
+ const updatePanelFrame = () => {
336
+ const trigger = triggerRef.current;
337
+ if (!trigger)
338
+ return;
339
+ const rect = trigger.getBoundingClientRect();
340
+ const viewportWidth = window.innerWidth;
341
+ const viewportHeight = window.innerHeight;
342
+ const width = Math.min(Math.max(rect.width, 380), Math.max(280, viewportWidth - 16));
343
+ const left = Math.min(Math.max(8, rect.left), Math.max(8, viewportWidth - width - 8));
344
+ const spaceBelow = viewportHeight - rect.bottom - 12;
345
+ const spaceAbove = rect.top - 12;
346
+ const openBelow = spaceBelow >= 280 || spaceBelow >= spaceAbove;
347
+ const maxHeight = Math.min(420, Math.max(openBelow ? spaceBelow : spaceAbove, 180));
348
+ const top = openBelow
349
+ ? Math.min(rect.bottom + 6, viewportHeight - maxHeight - 8)
350
+ : Math.max(8, rect.top - maxHeight - 6);
351
+ setPanelFrame({ left, maxHeight, top, width });
352
+ };
353
+ updatePanelFrame();
354
+ searchRef.current?.focus();
355
+ window.addEventListener("resize", updatePanelFrame);
356
+ window.addEventListener("scroll", updatePanelFrame, true);
357
+ return () => {
358
+ window.removeEventListener("resize", updatePanelFrame);
359
+ window.removeEventListener("scroll", updatePanelFrame, true);
360
+ };
361
+ }, [open]);
362
+ React.useEffect(() => {
363
+ if (!open)
364
+ return;
365
+ const onPointerDown = (event) => {
366
+ const target = event.target;
367
+ if (triggerRef.current?.contains(target) ||
368
+ panelRef.current?.contains(target)) {
369
+ return;
370
+ }
371
+ setOpen(false);
372
+ };
373
+ document.addEventListener("mousedown", onPointerDown);
374
+ return () => document.removeEventListener("mousedown", onPointerDown);
375
+ }, [open]);
376
+ const selectAgent = (agent) => {
377
+ props.onChange(agent.slug);
378
+ setOpen(false);
379
+ setQuery("");
380
+ };
241
381
  const onKeyDown = (event) => {
242
382
  if (event.key === "Escape") {
383
+ setOpen(false);
243
384
  setQuery("");
385
+ triggerRef.current?.focus();
244
386
  return;
245
387
  }
246
388
  if (event.key === "ArrowDown") {
@@ -255,38 +397,124 @@ export function AgentSearchSelect(props) {
255
397
  }
256
398
  if (event.key === "Enter" && filtered[activeIndex]) {
257
399
  event.preventDefault();
258
- props.onChange(filtered[activeIndex].slug);
400
+ selectAgent(filtered[activeIndex]);
259
401
  }
260
402
  };
261
- return React.createElement("div", { className: "mcoda-agent-setup__agent-search", onKeyDown }, React.createElement("input", {
262
- value: query,
263
- onChange: (event) => setQuery(event.target.value),
264
- "aria-label": "Search agents",
265
- placeholder: "Search agent, model, provider, or slug",
266
- }), selected
267
- ? React.createElement("div", { className: "mcoda-agent-setup__selected-agent" }, selected.displayName ?? selected.slug)
268
- : null, React.createElement("div", null, `Showing all ${filtered.length} agents`), React.createElement("div", {
269
- className: "mcoda-agent-setup__agent-list",
270
- role: "listbox",
271
- style: { maxHeight: viewportHeight, overflowY: "auto" },
272
- onScroll: (event) => setScrollTop(event.currentTarget.scrollTop),
273
- }, React.createElement("div", { style: { height: windowed.beforeHeight } }), windowed.items.map((agent) => {
274
- const absoluteIndex = filtered.findIndex((candidate) => candidate.slug === agent.slug);
275
- const active = absoluteIndex === activeIndex;
276
- return (React.createElement("button", {
277
- key: agent.slug,
278
- type: "button",
279
- className: agent.slug === props.value || active
280
- ? "mcoda-agent-setup__agent-row mcoda-agent-setup__agent-row--selected"
281
- : "mcoda-agent-setup__agent-row",
282
- role: "option",
283
- "aria-selected": agent.slug === props.value,
284
- style: { minHeight: rowHeight },
285
- onClick: () => props.onChange(agent.slug),
286
- }, React.createElement("span", null, agent.displayName ?? agent.slug), React.createElement("small", null, [agent.model ?? agent.defaultModel, agent.provider, agent.healthStatus]
287
- .filter(Boolean)
288
- .join(" / ")), React.createElement("code", null, agent.slug)));
289
- }), React.createElement("div", { style: { height: windowed.afterHeight } })));
403
+ return React.createElement("div", { className: "mcoda-agent-setup__agent-combobox" }, React.createElement("button", {
404
+ ref: triggerRef,
405
+ type: "button",
406
+ className: "mcoda-agent-setup__agent-trigger",
407
+ "aria-haspopup": "listbox",
408
+ "aria-expanded": open,
409
+ disabled: props.agents.length === 0,
410
+ onClick: () => setOpen((current) => !current),
411
+ }, React.createElement("span", { className: "mcoda-agent-setup__agent-trigger-main" }, selected
412
+ ? React.createElement(React.Fragment, null, React.createElement("strong", null, agentTitle(selected)), React.createElement("small", null, agentMetadata(selected)))
413
+ : React.createElement("strong", null, props.agents.length ? "Select agent" : "No agents synced")), React.createElement("span", { className: "mcoda-agent-setup__chevron" }, "⌄")), open
414
+ ? React.createElement("div", {
415
+ className: "mcoda-agent-setup__agent-panel",
416
+ ref: panelRef,
417
+ style: {
418
+ left: panelFrame.left,
419
+ maxHeight: panelFrame.maxHeight,
420
+ top: panelFrame.top,
421
+ width: panelFrame.width,
422
+ },
423
+ }, React.createElement("div", { className: "mcoda-agent-setup__agent-panel-search" }, React.createElement("input", {
424
+ ref: searchRef,
425
+ value: query,
426
+ onChange: (event) => setQuery(event.target.value),
427
+ onKeyDown,
428
+ "aria-label": "Search agents",
429
+ placeholder: "Search agent, model, provider, or slug",
430
+ })), React.createElement("div", { className: "mcoda-agent-setup__agent-count" }, filtered.length
431
+ ? `Showing all ${filtered.length} agents`
432
+ : "No agents match this search"), React.createElement("div", {
433
+ className: "mcoda-agent-setup__agent-list",
434
+ role: "listbox",
435
+ style: { maxHeight: viewportHeight, overflowY: "auto" },
436
+ onKeyDown,
437
+ onScroll: (event) => setScrollTop(event.currentTarget.scrollTop),
438
+ }, React.createElement("div", { style: { height: windowed.beforeHeight } }), windowed.items.map((agent, index) => {
439
+ const absoluteIndex = windowed.startIndex + index;
440
+ const active = absoluteIndex === activeIndex;
441
+ return React.createElement("button", {
442
+ key: agent.slug,
443
+ type: "button",
444
+ className: agent.slug === props.value || active
445
+ ? "mcoda-agent-setup__agent-row mcoda-agent-setup__agent-row--selected"
446
+ : "mcoda-agent-setup__agent-row",
447
+ role: "option",
448
+ "aria-selected": agent.slug === props.value,
449
+ style: { minHeight: rowHeight },
450
+ onMouseEnter: () => setActiveIndex(absoluteIndex),
451
+ onClick: () => selectAgent(agent),
452
+ }, React.createElement("span", { className: "mcoda-agent-setup__agent-row-main" }, React.createElement("strong", null, agentTitle(agent)), React.createElement("small", null, agentMetadata(agent)), React.createElement("code", null, agent.slug)), agent.slug === props.value
453
+ ? React.createElement("span", { className: "mcoda-agent-setup__selected-mark" }, "Selected")
454
+ : null);
455
+ }), React.createElement("div", { style: { height: windowed.afterHeight } })))
456
+ : null);
457
+ }
458
+ function MetricCard(props) {
459
+ return React.createElement("div", { className: "mcoda-agent-setup__metric" }, React.createElement("span", null, props.label), React.createElement("strong", null, props.value), React.createElement("small", null, props.detail));
460
+ }
461
+ function AgentStatusBadge(props) {
462
+ const kind = props.agent?.managedKind;
463
+ const sourceLabel = kind === "cloud"
464
+ ? "Cloud"
465
+ : kind === "self_hosted"
466
+ ? "Self-hosted"
467
+ : props.agent
468
+ ? "Local"
469
+ : props.slug
470
+ ? "Missing"
471
+ : "Unset";
472
+ const className = kind === "cloud"
473
+ ? "mcoda-agent-setup__badge mcoda-agent-setup__badge--info"
474
+ : kind === "self_hosted"
475
+ ? "mcoda-agent-setup__badge mcoda-agent-setup__badge--success"
476
+ : props.agent
477
+ ? "mcoda-agent-setup__badge"
478
+ : "mcoda-agent-setup__badge mcoda-agent-setup__badge--warning";
479
+ return React.createElement("span", { className }, sourceLabel);
480
+ }
481
+ function firstSelfHostedAgentSlug(servers) {
482
+ return servers.find((server) => server.agents.length > 0)?.agents[0]?.slug ?? null;
483
+ }
484
+ function agentTitle(agent) {
485
+ return agent.displayName ?? prettySlug(agent.slug);
486
+ }
487
+ function agentMetadata(agent) {
488
+ return ([
489
+ agent.defaultModel ?? agent.model,
490
+ agent.provider,
491
+ agent.healthStatus && agent.healthStatus !== "-" ? agent.healthStatus : null,
492
+ ]
493
+ .filter(Boolean)
494
+ .join(" / ") || agent.slug);
495
+ }
496
+ function prettySlug(slug) {
497
+ return slug
498
+ .replace(/^mswarm-cloud-/, "")
499
+ .replace(/^mswarm-self-hosted-/, "")
500
+ .replace(/^mcoda-/, "")
501
+ .replace(/[-_]+/g, " ");
502
+ }
503
+ function formatTimestamp(value) {
504
+ if (!value)
505
+ return "never";
506
+ const parsed = Date.parse(value);
507
+ if (Number.isNaN(parsed))
508
+ return value;
509
+ const deltaMinutes = Math.max(0, Math.round((Date.now() - parsed) / 60000));
510
+ if (deltaMinutes < 1)
511
+ return "just now";
512
+ if (deltaMinutes < 60)
513
+ return `${deltaMinutes}m ago`;
514
+ const deltaHours = Math.round(deltaMinutes / 60);
515
+ if (deltaHours < 24)
516
+ return `${deltaHours}h ago`;
517
+ return `${Math.round(deltaHours / 24)}d ago`;
290
518
  }
291
519
  function joinClasses(...values) {
292
520
  return values.filter(Boolean).join(" ");
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,4BAA4B,EAC5B,sBAAsB,EAGvB,MAAM,aAAa,CAAC;AAErB,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,4BAA4B,GACpC,sBAAsB,CAqJxB"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAGV,4BAA4B,EAC5B,sBAAsB,EAGvB,MAAM,aAAa,CAAC;AAIrB,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,4BAA4B,GACpC,sBAAsB,CAoLxB"}
@@ -1,6 +1,7 @@
1
1
  import { defaultMcodaStageDefinitions } from "../defaultStages.js";
2
2
  import { buildCloudAgentOptions, buildSelfHostedServerOptions, } from "../headless/catalog.js";
3
3
  import { createProgrammaticMcodaRuntimeAdapter } from "./programmaticRuntime.js";
4
+ const REMOTE_ATTEMPTS = 3;
4
5
  export function createMcodaAgentSetupService(options) {
5
6
  const runtime = options.mcoda ?? createProgrammaticMcodaRuntimeAdapter();
6
7
  const stages = options.defaultStages ?? defaultMcodaStageDefinitions;
@@ -8,29 +9,43 @@ export function createMcodaAgentSetupService(options) {
8
9
  const authorize = async (request) => {
9
10
  await options.authorize?.(request);
10
11
  };
11
- const capture = async (errors, key, fn, fallback) => {
12
- try {
13
- return await fn();
14
- }
15
- catch (error) {
16
- const message = error instanceof Error ? error.message : String(error);
17
- errors[key] = message;
18
- options.logger?.warn?.(`mcoda agent setup ${key} failed`, {
19
- error: message,
20
- });
21
- return fallback;
12
+ const capture = async (errors, key, fn, fallback, attempts = 1) => {
13
+ let lastError;
14
+ for (let attempt = 1; attempt <= Math.max(1, attempts); attempt += 1) {
15
+ try {
16
+ return await fn();
17
+ }
18
+ catch (error) {
19
+ lastError = error;
20
+ if (attempt < attempts) {
21
+ await sleep(150 * attempt);
22
+ }
23
+ }
22
24
  }
25
+ const message = lastError instanceof Error ? lastError.message : String(lastError);
26
+ errors[key] = message;
27
+ options.logger?.warn?.(`mcoda agent setup ${key} failed`, {
28
+ error: message,
29
+ });
30
+ return fallback;
31
+ };
32
+ const captureRemote = async (errors, key, fn, fallback) => {
33
+ return capture(errors, key, fn, fallback, REMOTE_ATTEMPTS);
23
34
  };
24
35
  const buildSnapshot = async (extraErrors = {}) => {
25
36
  const settings = await options.settingsStore.load();
26
37
  const errors = { ...extraErrors };
27
38
  const [localAgents, cloudCatalogAgents, selfHostedCatalogAgents] = await Promise.all([
28
39
  capture(errors, "local_agents", () => runtime.listLocalAgents({ refreshHealth: true }), []),
29
- capture(errors, "cloud_agents", () => runtime.listCloudAgents(), []),
30
- capture(errors, "self_hosted_agents", () => runtime.listSelfHostedAgents({ includeUnreachable: true }), []),
40
+ captureRemote(errors, "cloud_agents", () => runtime.listCloudAgents(), []),
41
+ captureRemote(errors, "self_hosted_agents", () => runtime.listSelfHostedAgents({ includeUnreachable: true }), []),
31
42
  ]);
32
- const cloudAgents = buildCloudAgentOptions(localAgents, cloudCatalogAgents);
33
- const selfHostedServers = buildSelfHostedServerOptions(localAgents, selfHostedCatalogAgents);
43
+ const cloudAgents = errors.cloud_agents === undefined
44
+ ? buildCloudAgentOptions(localAgents, cloudCatalogAgents)
45
+ : [];
46
+ const selfHostedServers = errors.self_hosted_agents === undefined
47
+ ? buildSelfHostedServerOptions(localAgents, selfHostedCatalogAgents)
48
+ : [];
34
49
  const catalog = {
35
50
  localAgents,
36
51
  cloudAgents,
@@ -112,14 +127,17 @@ export function createMcodaAgentSetupService(options) {
112
127
  };
113
128
  async function syncCatalogs() {
114
129
  const errors = {};
115
- await capture(errors, "cloud_agent_sync", () => runtime.syncCloudAgents({ pruneMissing: true }), []);
116
- await capture(errors, "self_hosted_agent_sync", () => runtime.syncSelfHostedAgents({
130
+ await captureRemote(errors, "cloud_agent_sync", () => runtime.syncCloudAgents({ pruneMissing: true }), []);
131
+ await captureRemote(errors, "self_hosted_agent_sync", () => runtime.syncSelfHostedAgents({
117
132
  pruneMissing: true,
118
133
  includeUnreachable: true,
119
134
  }), []);
120
135
  return errors;
121
136
  }
122
137
  }
138
+ function sleep(ms) {
139
+ return new Promise((resolve) => setTimeout(resolve, ms));
140
+ }
123
141
  function initialAssignments(stages) {
124
142
  return Object.fromEntries(stages.map((stage) => [stage.stageKey, stage.defaultAgentSlug ?? null]));
125
143
  }
@@ -32,3 +32,7 @@ app.all("/api/mcoda/*", async (req, res) => {
32
32
 
33
33
  app.listen(3000);
34
34
  ```
35
+
36
+ This uses the real programmatic mcoda/mswarm runtime by default. After the
37
+ frontend posts a real mswarm API key to `/api/mcoda/mswarm-api-key`, cloud and
38
+ self-hosted agents are fetched and synced from the real mswarm server.
@@ -12,6 +12,7 @@ import {
12
12
  createMcodaAgentSetupClient,
13
13
  defaultMcodaStageDefinitions
14
14
  } from "@mcoda/agent-setup/react";
15
+ import "@mcoda/agent-setup/react/styles.css";
15
16
 
16
17
  const client = createMcodaAgentSetupClient({
17
18
  baseUrl: "/api/mcoda",
@@ -6,6 +6,7 @@ import {
6
6
  createMcodaAgentSetupClient,
7
7
  defaultMcodaStageDefinitions
8
8
  } from "@mcoda/agent-setup/react";
9
+ import "@mcoda/agent-setup/react/styles.css";
9
10
 
10
11
  const client = createMcodaAgentSetupClient({
11
12
  baseUrl: "/api/mcoda"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcoda/agent-setup",
3
- "version": "0.1.66",
3
+ "version": "0.1.70",
4
4
  "description": "Turnkey mcoda/mswarm agent setup SDK for applications.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,11 +21,15 @@
21
21
  "./react": {
22
22
  "types": "./dist/react/index.d.ts",
23
23
  "import": "./dist/react/index.js"
24
+ },
25
+ "./react/styles.css": {
26
+ "default": "./styles/react.css"
24
27
  }
25
28
  },
26
29
  "files": [
27
30
  "dist",
28
31
  "examples",
32
+ "styles",
29
33
  "!dist/**/__tests__/**",
30
34
  "!dist/**/*.test.*",
31
35
  "README.md",
@@ -58,7 +62,7 @@
58
62
  "access": "public"
59
63
  },
60
64
  "dependencies": {
61
- "@mcoda/core": "0.1.66"
65
+ "@mcoda/core": "0.1.70"
62
66
  },
63
67
  "peerDependencies": {
64
68
  "react": ">=18.2.0 || >=19.0.0",
@@ -0,0 +1,570 @@
1
+ .mcoda-agent-setup {
2
+ --mcoda-surface: #ffffff;
3
+ --mcoda-page: #f8fafc;
4
+ --mcoda-border: #e2e8f0;
5
+ --mcoda-border-strong: #cbd5e1;
6
+ --mcoda-text: #0f172a;
7
+ --mcoda-muted: #64748b;
8
+ --mcoda-faint: #94a3b8;
9
+ --mcoda-blue: #2563eb;
10
+ --mcoda-blue-soft: #eff6ff;
11
+ --mcoda-emerald: #059669;
12
+ --mcoda-emerald-soft: #ecfdf5;
13
+ --mcoda-amber: #d97706;
14
+ --mcoda-amber-soft: #fffbeb;
15
+ --mcoda-rose: #e11d48;
16
+ --mcoda-rose-soft: #fff1f2;
17
+ color: var(--mcoda-text);
18
+ display: grid;
19
+ gap: 16px;
20
+ width: 100%;
21
+ }
22
+
23
+ .mcoda-agent-setup *,
24
+ .mcoda-agent-setup *::before,
25
+ .mcoda-agent-setup *::after {
26
+ box-sizing: border-box;
27
+ }
28
+
29
+ .mcoda-agent-setup button,
30
+ .mcoda-agent-setup input,
31
+ .mcoda-agent-setup select {
32
+ font: inherit;
33
+ }
34
+
35
+ .mcoda-agent-setup__header,
36
+ .mcoda-agent-setup__summary,
37
+ .mcoda-agent-setup__access,
38
+ .mcoda-agent-setup__assignments,
39
+ .mcoda-agent-setup__empty-state,
40
+ .mcoda-agent-setup__alert {
41
+ background: var(--mcoda-surface);
42
+ border: 1px solid var(--mcoda-border);
43
+ border-radius: 8px;
44
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
45
+ }
46
+
47
+ .mcoda-agent-setup__header {
48
+ align-items: center;
49
+ display: flex;
50
+ gap: 16px;
51
+ justify-content: space-between;
52
+ padding: 18px 20px;
53
+ }
54
+
55
+ .mcoda-agent-setup__title-block {
56
+ min-width: 0;
57
+ }
58
+
59
+ .mcoda-agent-setup__eyebrow {
60
+ color: var(--mcoda-blue);
61
+ font-size: 10px;
62
+ font-weight: 800;
63
+ letter-spacing: 0;
64
+ margin: 0 0 4px;
65
+ text-transform: uppercase;
66
+ }
67
+
68
+ .mcoda-agent-setup__header h1,
69
+ .mcoda-agent-setup__card-heading h2,
70
+ .mcoda-agent-setup__assignments h2 {
71
+ color: var(--mcoda-text);
72
+ font-size: 18px;
73
+ font-weight: 750;
74
+ line-height: 1.2;
75
+ margin: 0;
76
+ }
77
+
78
+ .mcoda-agent-setup__header p,
79
+ .mcoda-agent-setup__card-heading p {
80
+ color: var(--mcoda-muted);
81
+ font-size: 13px;
82
+ line-height: 1.45;
83
+ margin: 4px 0 0;
84
+ }
85
+
86
+ .mcoda-agent-setup__button {
87
+ align-items: center;
88
+ border: 1px solid transparent;
89
+ border-radius: 8px;
90
+ cursor: pointer;
91
+ display: inline-flex;
92
+ font-size: 13px;
93
+ font-weight: 700;
94
+ gap: 8px;
95
+ justify-content: center;
96
+ min-height: 38px;
97
+ padding: 0 14px;
98
+ transition:
99
+ background-color 140ms ease,
100
+ border-color 140ms ease,
101
+ color 140ms ease;
102
+ white-space: nowrap;
103
+ }
104
+
105
+ .mcoda-agent-setup__button:disabled {
106
+ background: #f1f5f9;
107
+ color: var(--mcoda-faint);
108
+ cursor: not-allowed;
109
+ }
110
+
111
+ .mcoda-agent-setup__button--primary {
112
+ background: #0f172a;
113
+ color: #ffffff;
114
+ }
115
+
116
+ .mcoda-agent-setup__button--primary:hover:not(:disabled) {
117
+ background: #1e293b;
118
+ }
119
+
120
+ .mcoda-agent-setup__button--secondary {
121
+ background: var(--mcoda-blue-soft);
122
+ border-color: #bfdbfe;
123
+ color: #1d4ed8;
124
+ }
125
+
126
+ .mcoda-agent-setup__button--secondary:hover:not(:disabled) {
127
+ background: #dbeafe;
128
+ }
129
+
130
+ .mcoda-agent-setup__button--success {
131
+ background: var(--mcoda-emerald);
132
+ color: #ffffff;
133
+ }
134
+
135
+ .mcoda-agent-setup__button--success:hover:not(:disabled) {
136
+ background: #047857;
137
+ }
138
+
139
+ .mcoda-agent-setup__alert {
140
+ font-size: 13px;
141
+ margin: 0;
142
+ padding: 12px 14px;
143
+ }
144
+
145
+ .mcoda-agent-setup__alert--error {
146
+ background: var(--mcoda-rose-soft);
147
+ border-color: #fecdd3;
148
+ color: #be123c;
149
+ }
150
+
151
+ .mcoda-agent-setup__alert--success {
152
+ background: var(--mcoda-emerald-soft);
153
+ border-color: #a7f3d0;
154
+ color: #047857;
155
+ }
156
+
157
+ .mcoda-agent-setup__overview {
158
+ display: grid;
159
+ gap: 16px;
160
+ grid-template-columns: minmax(0, 1.4fr) minmax(300px, 0.75fr);
161
+ }
162
+
163
+ .mcoda-agent-setup__summary,
164
+ .mcoda-agent-setup__access,
165
+ .mcoda-agent-setup__assignments,
166
+ .mcoda-agent-setup__empty-state {
167
+ padding: 18px;
168
+ }
169
+
170
+ .mcoda-agent-setup__card-heading {
171
+ align-items: flex-start;
172
+ display: flex;
173
+ gap: 14px;
174
+ justify-content: space-between;
175
+ margin-bottom: 16px;
176
+ }
177
+
178
+ .mcoda-agent-setup__metric-grid {
179
+ display: grid;
180
+ gap: 12px;
181
+ grid-template-columns: repeat(5, minmax(120px, 1fr));
182
+ }
183
+
184
+ .mcoda-agent-setup__metric {
185
+ background: #f8fafc;
186
+ border: 1px solid #edf2f7;
187
+ border-radius: 8px;
188
+ min-width: 0;
189
+ padding: 12px;
190
+ }
191
+
192
+ .mcoda-agent-setup__metric span,
193
+ .mcoda-agent-setup__field span,
194
+ .mcoda-agent-setup__stage-table th {
195
+ color: var(--mcoda-faint);
196
+ display: block;
197
+ font-size: 10px;
198
+ font-weight: 800;
199
+ letter-spacing: 0;
200
+ text-transform: uppercase;
201
+ }
202
+
203
+ .mcoda-agent-setup__metric strong {
204
+ color: var(--mcoda-text);
205
+ display: block;
206
+ font-size: 22px;
207
+ font-weight: 800;
208
+ line-height: 1.1;
209
+ margin-top: 8px;
210
+ overflow: hidden;
211
+ text-overflow: ellipsis;
212
+ white-space: nowrap;
213
+ }
214
+
215
+ .mcoda-agent-setup__metric small {
216
+ color: var(--mcoda-muted);
217
+ display: block;
218
+ font-size: 12px;
219
+ margin-top: 6px;
220
+ overflow: hidden;
221
+ text-overflow: ellipsis;
222
+ white-space: nowrap;
223
+ }
224
+
225
+ .mcoda-agent-setup__badge {
226
+ background: #f1f5f9;
227
+ border: 1px solid #e2e8f0;
228
+ border-radius: 999px;
229
+ color: #475569;
230
+ display: inline-flex;
231
+ font-size: 10px;
232
+ font-weight: 800;
233
+ letter-spacing: 0;
234
+ line-height: 1;
235
+ padding: 6px 8px;
236
+ text-transform: uppercase;
237
+ white-space: nowrap;
238
+ }
239
+
240
+ .mcoda-agent-setup__badge--success {
241
+ background: var(--mcoda-emerald-soft);
242
+ border-color: #a7f3d0;
243
+ color: #047857;
244
+ }
245
+
246
+ .mcoda-agent-setup__badge--warning {
247
+ background: var(--mcoda-amber-soft);
248
+ border-color: #fde68a;
249
+ color: #b45309;
250
+ }
251
+
252
+ .mcoda-agent-setup__badge--info {
253
+ background: var(--mcoda-blue-soft);
254
+ border-color: #bfdbfe;
255
+ color: #1d4ed8;
256
+ }
257
+
258
+ .mcoda-agent-setup__warnings {
259
+ background: var(--mcoda-amber-soft);
260
+ border: 1px solid #fde68a;
261
+ border-radius: 8px;
262
+ color: #92400e;
263
+ font-size: 12px;
264
+ margin: 14px 0 0;
265
+ padding: 10px 12px 10px 28px;
266
+ }
267
+
268
+ .mcoda-agent-setup__access {
269
+ align-content: start;
270
+ display: grid;
271
+ gap: 14px;
272
+ }
273
+
274
+ .mcoda-agent-setup__field {
275
+ display: grid;
276
+ gap: 8px;
277
+ }
278
+
279
+ .mcoda-agent-setup__field input,
280
+ .mcoda-agent-setup__server-select input,
281
+ .mcoda-agent-setup__server-select select,
282
+ .mcoda-agent-setup__agent-panel-search input {
283
+ background: #ffffff;
284
+ border: 1px solid var(--mcoda-border);
285
+ border-radius: 8px;
286
+ color: var(--mcoda-text);
287
+ min-height: 40px;
288
+ outline: none;
289
+ padding: 0 12px;
290
+ width: 100%;
291
+ }
292
+
293
+ .mcoda-agent-setup__field input:focus,
294
+ .mcoda-agent-setup__server-select input:focus,
295
+ .mcoda-agent-setup__server-select select:focus,
296
+ .mcoda-agent-setup__agent-panel-search input:focus,
297
+ .mcoda-agent-setup__agent-trigger:focus-visible,
298
+ .mcoda-agent-setup__button:focus-visible,
299
+ .mcoda-agent-setup__source button:focus-visible {
300
+ border-color: #60a5fa;
301
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.14);
302
+ outline: none;
303
+ }
304
+
305
+ .mcoda-agent-setup__action-row {
306
+ display: flex;
307
+ flex-wrap: wrap;
308
+ gap: 10px;
309
+ }
310
+
311
+ .mcoda-agent-setup__stage-table-wrap {
312
+ border: 1px solid var(--mcoda-border);
313
+ border-radius: 8px;
314
+ overflow-x: auto;
315
+ }
316
+
317
+ .mcoda-agent-setup__stage-table {
318
+ border-collapse: collapse;
319
+ min-width: 980px;
320
+ table-layout: fixed;
321
+ width: 100%;
322
+ }
323
+
324
+ .mcoda-agent-setup__stage-table th {
325
+ background: #f8fafc;
326
+ border-bottom: 1px solid var(--mcoda-border);
327
+ padding: 12px;
328
+ text-align: left;
329
+ }
330
+
331
+ .mcoda-agent-setup__stage-table td {
332
+ border-bottom: 1px solid #f1f5f9;
333
+ padding: 12px;
334
+ vertical-align: top;
335
+ }
336
+
337
+ .mcoda-agent-setup__stage-table tr:last-child td {
338
+ border-bottom: 0;
339
+ }
340
+
341
+ .mcoda-agent-setup__stage-table th:nth-child(1),
342
+ .mcoda-agent-setup__stage-table td:nth-child(1) {
343
+ width: 22%;
344
+ }
345
+
346
+ .mcoda-agent-setup__stage-table th:nth-child(2),
347
+ .mcoda-agent-setup__stage-table td:nth-child(2) {
348
+ width: 15%;
349
+ }
350
+
351
+ .mcoda-agent-setup__stage-table th:nth-child(3),
352
+ .mcoda-agent-setup__stage-table td:nth-child(3) {
353
+ width: 18%;
354
+ }
355
+
356
+ .mcoda-agent-setup__stage-table th:nth-child(4),
357
+ .mcoda-agent-setup__stage-table td:nth-child(4) {
358
+ width: 34%;
359
+ }
360
+
361
+ .mcoda-agent-setup__stage-cell strong {
362
+ color: var(--mcoda-text);
363
+ display: block;
364
+ font-size: 14px;
365
+ }
366
+
367
+ .mcoda-agent-setup__stage-cell p {
368
+ color: var(--mcoda-muted);
369
+ font-size: 12px;
370
+ line-height: 1.4;
371
+ margin: 4px 0 8px;
372
+ }
373
+
374
+ .mcoda-agent-setup__stage-cell code,
375
+ .mcoda-agent-setup__agent-row code {
376
+ color: var(--mcoda-faint);
377
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
378
+ font-size: 11px;
379
+ }
380
+
381
+ .mcoda-agent-setup__source {
382
+ background: #f1f5f9;
383
+ border: 1px solid var(--mcoda-border);
384
+ border-radius: 8px;
385
+ display: inline-flex;
386
+ gap: 4px;
387
+ padding: 4px;
388
+ }
389
+
390
+ .mcoda-agent-setup__source button {
391
+ background: transparent;
392
+ border: 0;
393
+ border-radius: 6px;
394
+ color: var(--mcoda-muted);
395
+ cursor: pointer;
396
+ font-size: 12px;
397
+ font-weight: 800;
398
+ min-height: 30px;
399
+ padding: 0 10px;
400
+ }
401
+
402
+ .mcoda-agent-setup__source button[aria-pressed="true"] {
403
+ background: #ffffff;
404
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1);
405
+ color: var(--mcoda-blue);
406
+ }
407
+
408
+ .mcoda-agent-setup__server-select {
409
+ display: grid;
410
+ gap: 8px;
411
+ }
412
+
413
+ .mcoda-agent-setup__muted-pill {
414
+ background: #f8fafc;
415
+ border: 1px solid var(--mcoda-border);
416
+ border-radius: 8px;
417
+ color: var(--mcoda-muted);
418
+ display: inline-flex;
419
+ font-size: 13px;
420
+ font-weight: 650;
421
+ min-height: 40px;
422
+ padding: 10px 12px;
423
+ width: 100%;
424
+ }
425
+
426
+ .mcoda-agent-setup__agent-combobox {
427
+ min-width: 0;
428
+ position: relative;
429
+ }
430
+
431
+ .mcoda-agent-setup__agent-trigger {
432
+ align-items: center;
433
+ background: #ffffff;
434
+ border: 1px solid var(--mcoda-border);
435
+ border-radius: 8px;
436
+ color: var(--mcoda-text);
437
+ cursor: pointer;
438
+ display: flex;
439
+ gap: 10px;
440
+ justify-content: space-between;
441
+ min-height: 44px;
442
+ padding: 8px 10px;
443
+ text-align: left;
444
+ width: 100%;
445
+ }
446
+
447
+ .mcoda-agent-setup__agent-trigger:disabled {
448
+ background: #f8fafc;
449
+ color: var(--mcoda-faint);
450
+ cursor: not-allowed;
451
+ }
452
+
453
+ .mcoda-agent-setup__agent-trigger-main {
454
+ min-width: 0;
455
+ }
456
+
457
+ .mcoda-agent-setup__agent-trigger-main strong,
458
+ .mcoda-agent-setup__agent-row-main strong {
459
+ display: block;
460
+ font-size: 13px;
461
+ overflow: hidden;
462
+ text-overflow: ellipsis;
463
+ white-space: nowrap;
464
+ }
465
+
466
+ .mcoda-agent-setup__agent-trigger-main small,
467
+ .mcoda-agent-setup__agent-row-main small {
468
+ color: var(--mcoda-muted);
469
+ display: block;
470
+ font-size: 11px;
471
+ margin-top: 3px;
472
+ overflow: hidden;
473
+ text-overflow: ellipsis;
474
+ white-space: nowrap;
475
+ }
476
+
477
+ .mcoda-agent-setup__chevron {
478
+ color: var(--mcoda-faint);
479
+ font-size: 16px;
480
+ line-height: 1;
481
+ }
482
+
483
+ .mcoda-agent-setup__agent-panel {
484
+ background: #ffffff;
485
+ border: 1px solid var(--mcoda-border);
486
+ border-radius: 8px;
487
+ box-shadow: 0 24px 44px rgba(15, 23, 42, 0.18);
488
+ overflow: hidden;
489
+ position: fixed;
490
+ z-index: 90;
491
+ }
492
+
493
+ .mcoda-agent-setup__agent-panel-search {
494
+ border-bottom: 1px solid #f1f5f9;
495
+ padding: 8px;
496
+ }
497
+
498
+ .mcoda-agent-setup__agent-count {
499
+ color: var(--mcoda-muted);
500
+ font-size: 12px;
501
+ font-weight: 650;
502
+ padding: 8px 12px;
503
+ }
504
+
505
+ .mcoda-agent-setup__agent-list {
506
+ overflow-y: auto;
507
+ }
508
+
509
+ .mcoda-agent-setup__agent-row {
510
+ align-items: flex-start;
511
+ background: #ffffff;
512
+ border: 0;
513
+ border-top: 1px solid #f1f5f9;
514
+ color: var(--mcoda-text);
515
+ cursor: pointer;
516
+ display: flex;
517
+ gap: 12px;
518
+ justify-content: space-between;
519
+ padding: 9px 12px;
520
+ text-align: left;
521
+ width: 100%;
522
+ }
523
+
524
+ .mcoda-agent-setup__agent-row:hover,
525
+ .mcoda-agent-setup__agent-row--selected {
526
+ background: var(--mcoda-blue-soft);
527
+ }
528
+
529
+ .mcoda-agent-setup__agent-row-main {
530
+ min-width: 0;
531
+ }
532
+
533
+ .mcoda-agent-setup__selected-mark {
534
+ color: var(--mcoda-blue);
535
+ flex: none;
536
+ font-size: 10px;
537
+ font-weight: 800;
538
+ margin-top: 3px;
539
+ text-transform: uppercase;
540
+ }
541
+
542
+ .mcoda-agent-setup__empty-state {
543
+ color: var(--mcoda-muted);
544
+ font-size: 14px;
545
+ padding: 28px;
546
+ text-align: center;
547
+ }
548
+
549
+ @media (max-width: 1100px) {
550
+ .mcoda-agent-setup__overview,
551
+ .mcoda-agent-setup__metric-grid {
552
+ grid-template-columns: 1fr;
553
+ }
554
+
555
+ .mcoda-agent-setup__metric-grid {
556
+ grid-template-columns: repeat(2, minmax(0, 1fr));
557
+ }
558
+ }
559
+
560
+ @media (max-width: 720px) {
561
+ .mcoda-agent-setup__header,
562
+ .mcoda-agent-setup__card-heading {
563
+ align-items: stretch;
564
+ flex-direction: column;
565
+ }
566
+
567
+ .mcoda-agent-setup__metric-grid {
568
+ grid-template-columns: 1fr;
569
+ }
570
+ }