@growthub/cli 0.13.9 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/env-status/route.js +31 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +227 -5
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/query/route.js +1 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +70 -9
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceActivationPanel.jsx +17 -1
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +6 -3
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +61 -35
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryCreationCockpit.jsx +200 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +414 -9
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +339 -77
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +81 -10
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +70 -85
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ReferencePicker.jsx +2 -2
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SidecarExpandView.jsx +37 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SwarmRunCockpit.jsx +625 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/helper-commands.js +150 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +229 -9
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +224 -14
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +2 -4
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-agent-host.js +139 -4
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +4 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/api-registry-creation-flow.js +317 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/api-response-profile.js +207 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/creation-error-recovery.js +103 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/env-status.js +100 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +246 -4
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +69 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +411 -1
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +215 -0
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/server-resolver-write.js +67 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/serverless-upgrade.js +89 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-activation.js +11 -4
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +8 -1
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +30 -1
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +8 -6
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-resolver-proposal.js +200 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-swarm-proposal.js +551 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -1
  39. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useMemo } from "react";
4
- import { Plus, Trash2 } from "lucide-react";
4
+ import { Check, Plus, Trash2 } from "lucide-react";
5
5
  import { HOST_AUTH_CATALOG } from "@/lib/sandbox-agent-host-catalog";
6
6
 
7
7
  function getHostOptions() {
@@ -11,31 +11,63 @@ function getHostOptions() {
11
11
  }));
12
12
  }
13
13
 
14
- function patchOrchestrator(graph, patch) {
14
+ function nodeSandboxRecordRef(objectId, rowName, nodeId) {
15
+ return {
16
+ objectId: String(objectId || "").trim(),
17
+ rowName: String(rowName || "").trim(),
18
+ nodeId: String(nodeId || "").trim()
19
+ };
20
+ }
21
+
22
+ function withRecordRef(patch, objectId, rowName, nodeId) {
23
+ return {
24
+ ...patch,
25
+ sandboxRecordRef: nodeSandboxRecordRef(objectId, rowName, nodeId)
26
+ };
27
+ }
28
+
29
+ function WorkflowCheckbox({ checked, disabled, onChange, children, title }) {
30
+ return (
31
+ <label className="dm-orchestration-config__field dm-orchestration-config__field-inline dm-workflow-check" title={title}>
32
+ <input
33
+ type="checkbox"
34
+ checked={checked}
35
+ disabled={disabled}
36
+ onChange={(event) => onChange?.(event.target.checked)}
37
+ />
38
+ <span className="dm-workflow-check__box" aria-hidden="true">
39
+ {checked ? <Check size={13} strokeWidth={2.4} /> : null}
40
+ </span>
41
+ <span>{children}</span>
42
+ </label>
43
+ );
44
+ }
45
+
46
+ function patchOrchestrator(graph, patch, objectId, rowName) {
15
47
  const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
16
48
  return {
17
49
  ...graph,
18
50
  nodes: nodes.map((node) =>
19
51
  node?.type === "thinAdapter"
20
- ? { ...node, config: { ...(node.config || {}), ...patch } }
52
+ ? { ...node, config: { ...(node.config || {}), ...withRecordRef(patch, objectId, rowName, node.id) } }
21
53
  : node
22
54
  )
23
55
  };
24
56
  }
25
57
 
26
- function patchSynthesis(graph, patch) {
58
+ function patchSynthesis(graph, patch, objectId, rowName) {
27
59
  const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
28
60
  return {
29
61
  ...graph,
30
62
  nodes: nodes.map((node) =>
31
63
  node?.type === "tool-result"
32
- ? { ...node, config: { ...(node.config || {}), ...patch } }
64
+ ? { ...node, config: { ...(node.config || {}), ...withRecordRef(patch, objectId, rowName, node.id) } }
33
65
  : node
34
66
  )
35
67
  };
36
68
  }
37
69
 
38
- function patchSubagent(graph, nodeId, patch) {
70
+ function patchSubagent(graph, nodeId, patch, objectId, rowName) {
39
71
  const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
40
72
  return {
41
73
  ...graph,
@@ -44,7 +76,7 @@ function patchSubagent(graph, nodeId, patch) {
44
76
  ? {
45
77
  ...node,
46
78
  label: patch.role != null ? String(patch.role) : node.label,
47
- config: { ...(node.config || {}), ...patch }
79
+ config: { ...(node.config || {}), ...withRecordRef(patch, objectId, rowName, node.id) }
48
80
  }
49
81
  : node
50
82
  )
@@ -107,7 +139,7 @@ function patchSwarmConfig(graph, patch) {
107
139
  return { ...graph, swarm: { ...base, ...patch } };
108
140
  }
109
141
 
110
- export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
142
+ export function AgentSwarmPanel({ graph, objectId, rowName, onGraphChange, disabled }) {
111
143
  const hostOptions = useMemo(getHostOptions, []);
112
144
  if (!graph || typeof graph !== "object") return null;
113
145
 
@@ -133,7 +165,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
133
165
  rows={3}
134
166
  value={orchestrator?.config?.prompt || ""}
135
167
  disabled={disabled || !orchestrator}
136
- onChange={(e) => patchGraph((g) => patchOrchestrator(g, { prompt: e.target.value }))}
168
+ onChange={(e) => patchGraph((g) => patchOrchestrator(g, { prompt: e.target.value }, objectId, rowName))}
137
169
  />
138
170
  </label>
139
171
  </div>
@@ -163,7 +195,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
163
195
  placeholder="Role"
164
196
  value={cfg.role || node.label || ""}
165
197
  disabled={disabled}
166
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { role: e.target.value }))}
198
+ onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { role: e.target.value }, objectId, rowName))}
167
199
  />
168
200
  <button
169
201
  type="button"
@@ -181,7 +213,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
181
213
  placeholder="One-sentence charter"
182
214
  value={cfg.description || ""}
183
215
  disabled={disabled}
184
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { description: e.target.value }))}
216
+ onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { description: e.target.value }, objectId, rowName))}
185
217
  />
186
218
  </label>
187
219
  <label className="dm-orchestration-config__field">
@@ -190,7 +222,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
190
222
  rows={2}
191
223
  value={cfg.taskPrompt || ""}
192
224
  disabled={disabled}
193
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { taskPrompt: e.target.value }))}
225
+ onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { taskPrompt: e.target.value }, objectId, rowName))}
194
226
  />
195
227
  </label>
196
228
  <label className="dm-orchestration-config__field">
@@ -201,7 +233,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
201
233
  disabled={disabled}
202
234
  onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, {
203
235
  tools: e.target.value.split(",").map((t) => t.trim()).filter(Boolean)
204
- }))}
236
+ }, objectId, rowName))}
205
237
  />
206
238
  </label>
207
239
  <label className="dm-orchestration-config__field">
@@ -212,7 +244,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
212
244
  placeholder="0 = inherit"
213
245
  value={cfg.maxTokens || 0}
214
246
  disabled={disabled}
215
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { maxTokens: Math.max(0, Number(e.target.value) || 0) }))}
247
+ onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { maxTokens: Math.max(0, Number(e.target.value) || 0) }, objectId, rowName))}
216
248
  />
217
249
  </label>
218
250
  <label className="dm-orchestration-config__field">
@@ -220,7 +252,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
220
252
  <select
221
253
  value={cfg.agentHost || ""}
222
254
  disabled={disabled}
223
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { agentHost: e.target.value }))}
255
+ onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { agentHost: e.target.value }, objectId, rowName))}
224
256
  >
225
257
  <option value="">Inherit</option>
226
258
  {hostOptions.map((opt) => (
@@ -228,27 +260,21 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
228
260
  ))}
229
261
  </select>
230
262
  </label>
231
- <label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
232
- <input
233
- type="checkbox"
234
- checked={cfg.required !== false}
235
- disabled={disabled}
236
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { required: e.target.checked }))}
237
- />
238
- <span>Required</span>
239
- </label>
240
- <label
241
- className="dm-orchestration-config__field dm-orchestration-config__field-inline"
263
+ <WorkflowCheckbox
264
+ checked={cfg.required !== false}
265
+ disabled={disabled}
266
+ onChange={(checked) => patchGraph((g) => patchSubagent(g, node.id, { required: checked }, objectId, rowName))}
267
+ >
268
+ Required
269
+ </WorkflowCheckbox>
270
+ <WorkflowCheckbox
271
+ checked={cfg.networkAccess === true}
272
+ disabled={disabled}
242
273
  title="Network is granted only when both this and the row's networkAllow are on."
274
+ onChange={(checked) => patchGraph((g) => patchSubagent(g, node.id, { networkAccess: checked }, objectId, rowName))}
243
275
  >
244
- <input
245
- type="checkbox"
246
- checked={cfg.networkAccess === true}
247
- disabled={disabled}
248
- onChange={(e) => patchGraph((g) => patchSubagent(g, node.id, { networkAccess: e.target.checked }))}
249
- />
250
- <span>Network</span>
251
- </label>
276
+ Network
277
+ </WorkflowCheckbox>
252
278
  </div>
253
279
  );
254
280
  })}
@@ -315,7 +341,7 @@ export function AgentSwarmPanel({ graph, onGraphChange, disabled }) {
315
341
  rows={2}
316
342
  value={synthesis?.config?.outcomePrompt || ""}
317
343
  disabled={disabled}
318
- onChange={(e) => patchGraph((g) => patchSynthesis(g, { outcomePrompt: e.target.value }))}
344
+ onChange={(e) => patchGraph((g) => patchSynthesis(g, { outcomePrompt: e.target.value }, objectId, rowName))}
319
345
  />
320
346
  </label>
321
347
  </div>
@@ -0,0 +1,200 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+
5
+ /**
6
+ * ApiRegistryCreationCockpit — the governed creation interface for one API,
7
+ * rendered inside the existing api-registry record drawer (DataModelShell).
8
+ *
9
+ * Renders the ordered journey from `deriveApiRegistryCreationState`
10
+ * (lib/api-registry-creation-flow.js) plus, once the API is tested, the response
11
+ * Shape analysis (`profileApiResponse` / `recommendResolver`) and a receipts log
12
+ * of real actions. Every actionable step's button calls back into the drawer's
13
+ * existing governed handlers via `onAction(action)` — status, the highlighted
14
+ * "next" step, the activation score, and which buttons are live all come from
15
+ * derivation/real responses, never guessed.
16
+ *
17
+ * Visual language is the workspace's own: the `.dm-db-status` chip and `dm-btn-*`
18
+ * buttons. No invented colors, no new primitive.
19
+ */
20
+
21
+ const STEP_STATUS = {
22
+ complete: { mod: "ok", label: "Done" },
23
+ active: { mod: "warn", label: "Next" },
24
+ pending: { mod: "", label: "Pending" },
25
+ blocked: { mod: "", label: "Blocked" },
26
+ optional: { mod: "", label: "Optional" },
27
+ };
28
+
29
+ const RESOLVER_LEVEL = {
30
+ optional: { mod: "", label: "Resolver optional" },
31
+ recommended: { mod: "warn", label: "Resolver recommended" },
32
+ required: { mod: "bad", label: "Resolver required" },
33
+ };
34
+
35
+ function StatusChip({ mod, children, className = "" }) {
36
+ return (
37
+ <span className={`dm-db-status${mod ? ` ${mod}` : ""}${className ? ` ${className}` : ""}`}>
38
+ <span />
39
+ {children}
40
+ </span>
41
+ );
42
+ }
43
+
44
+ export function ApiRegistryCreationCockpit({
45
+ state,
46
+ onAction,
47
+ busyAction = "",
48
+ disabled = false,
49
+ profile = null,
50
+ resolverRec = null,
51
+ receipts = [],
52
+ dataSourcePreview = null,
53
+ eyebrow = "Governed creation",
54
+ defaultCollapsed = false,
55
+ hideWhenComplete = false,
56
+ onCollapsedChange,
57
+ }) {
58
+ const [collapsed, setCollapsed] = useState(defaultCollapsed);
59
+ useEffect(() => {
60
+ setCollapsed(defaultCollapsed || Boolean(hideWhenComplete && state?.complete));
61
+ }, [defaultCollapsed, hideWhenComplete, state?.complete, state?.integrationId]);
62
+ if (!state || !Array.isArray(state.steps)) return null;
63
+ if (hideWhenComplete && state.complete) return null;
64
+ const candidates = profile?.candidates || {};
65
+ const candidateEntries = Object.entries(candidates).filter(([, v]) => v);
66
+ const previewRow = dataSourcePreview?.row || null;
67
+ const toggleCollapsed = () => {
68
+ const next = !collapsed;
69
+ setCollapsed(next);
70
+ onCollapsedChange?.(next);
71
+ };
72
+ const runAction = (action) => {
73
+ if (action?.id === "edit") {
74
+ setCollapsed(true);
75
+ onCollapsedChange?.(true);
76
+ }
77
+ onAction?.(action);
78
+ };
79
+
80
+ return (
81
+ <section className={`dm-api-action-card dm-cockpit${collapsed ? " is-collapsed" : ""}`} aria-label="API creation journey">
82
+ <button
83
+ type="button"
84
+ className="dm-cockpit-head"
85
+ aria-expanded={!collapsed}
86
+ onClick={toggleCollapsed}
87
+ >
88
+ <div className="dm-api-action-card-body">
89
+ <p className="dm-api-action-card-eyebrow">{eyebrow} · {state.score}% activated</p>
90
+ <h3>{state.headline}</h3>
91
+ </div>
92
+ <span className="dm-cockpit-count">{state.completedCount}/{state.totalCount}</span>
93
+ </button>
94
+
95
+ {!collapsed && <ol className="dm-cockpit-steps">
96
+ {state.steps.map((step) => {
97
+ const meta = STEP_STATUS[step.status] || STEP_STATUS.pending;
98
+ const isNext = step.id === state.nextStepId;
99
+ const action = step.action;
100
+ const isBusy = action && busyAction === `${step.id}:${action.id}`;
101
+ return (
102
+ <li
103
+ key={step.id}
104
+ className={`dm-cockpit-step${isNext ? " dm-cockpit-step-next" : ""}${step.status === "blocked" ? " dm-cockpit-step-muted" : ""}`}
105
+ >
106
+ <StatusChip mod={meta.mod} className="dm-cockpit-step-chip">{meta.label}</StatusChip>
107
+ <div className="dm-cockpit-step-body">
108
+ <p className="dm-cockpit-step-label">{step.label}</p>
109
+ <p className="dm-cockpit-step-desc">{step.description}</p>
110
+ {step.hint ? <p className="dm-cockpit-step-hint">{step.hint}</p> : null}
111
+ </div>
112
+ {action ? (
113
+ <button
114
+ type="button"
115
+ className={isNext ? "dm-btn-primary-sm" : "dm-btn-outline"}
116
+ disabled={disabled || Boolean(busyAction)}
117
+ onClick={() => runAction(action)}
118
+ >
119
+ {isBusy ? "Working…" : action.label}
120
+ </button>
121
+ ) : null}
122
+ </li>
123
+ );
124
+ })}
125
+ </ol>}
126
+
127
+ {!collapsed && profile && profile.parsed ? (
128
+ <div className="dm-cockpit-shape">
129
+ <div className="dm-cockpit-shape-head">
130
+ <p className="dm-api-action-card-eyebrow">Response shape</p>
131
+ {resolverRec ? (
132
+ <StatusChip mod={(RESOLVER_LEVEL[resolverRec.level] || RESOLVER_LEVEL.optional).mod}>
133
+ {(RESOLVER_LEVEL[resolverRec.level] || RESOLVER_LEVEL.optional).label}
134
+ </StatusChip>
135
+ ) : null}
136
+ </div>
137
+ <p className="dm-cockpit-step-desc">
138
+ {profile.usable
139
+ ? `${profile.recordCount} record${profile.recordCount === 1 ? "" : "s"}${profile.arrayPath ? ` at "${profile.arrayPath}"` : " (top-level)"} · entity "${profile.suggestedEntityType}".`
140
+ : "No record array detected in the response."}
141
+ </p>
142
+ {resolverRec ? <p className="dm-cockpit-step-hint">{resolverRec.reason}</p> : null}
143
+ {candidateEntries.length ? (
144
+ <div className="dm-cockpit-fields">
145
+ {candidateEntries.map(([role, name]) => (
146
+ <span key={role} className="dm-cockpit-field"><b>{role}</b>{name}</span>
147
+ ))}
148
+ </div>
149
+ ) : null}
150
+ {profile.hasPagination ? (
151
+ <p className="dm-cockpit-step-hint">Pagination keys present — a resolver is needed to fetch every page.</p>
152
+ ) : null}
153
+ </div>
154
+ ) : null}
155
+
156
+ {!collapsed && previewRow ? (
157
+ <div className="dm-cockpit-shape">
158
+ <p className="dm-api-action-card-eyebrow">Data Source preview</p>
159
+ <p className="dm-cockpit-step-desc">
160
+ Create will add a live-backed Data Source object that references this API by <code>registryId</code> and writes records to the source-records sidecar. Nothing fetches until you Refresh.
161
+ </p>
162
+ <div className="dm-cockpit-fields">
163
+ <span className="dm-cockpit-field"><b>name</b>{previewRow.Name}</span>
164
+ <span className="dm-cockpit-field"><b>sourceId</b>{previewRow.sourceId}</span>
165
+ <span className="dm-cockpit-field"><b>storage</b>{previewRow.sourceStorage}</span>
166
+ <span className="dm-cockpit-field"><b>entity</b>{previewRow.entityType}</span>
167
+ <span className="dm-cockpit-field"><b>registryId</b>{previewRow.registryId}</span>
168
+ {previewRow.authRef ? <span className="dm-cockpit-field"><b>authRef</b>{previewRow.authRef}</span> : null}
169
+ </div>
170
+ {Array.isArray(dataSourcePreview.fields) && dataSourcePreview.fields.length ? (
171
+ <>
172
+ <p className="dm-cockpit-step-hint">Detected fields it will carry:</p>
173
+ <div className="dm-cockpit-fields">
174
+ {dataSourcePreview.fields.slice(0, 10).map((f) => (
175
+ <span key={f.name} className="dm-cockpit-field"><b>{f.role || f.type}</b>{f.name}</span>
176
+ ))}
177
+ </div>
178
+ </>
179
+ ) : null}
180
+ </div>
181
+ ) : null}
182
+
183
+ {!collapsed && Array.isArray(receipts) && receipts.length ? (
184
+ <div className="dm-cockpit-receipts">
185
+ <p className="dm-api-action-card-eyebrow">Receipts</p>
186
+ <ul>
187
+ {receipts.slice(0, 6).map((r, i) => (
188
+ <li key={`${r.at}-${i}`} className="dm-cockpit-receipt">
189
+ <StatusChip mod={r.ok ? "ok" : "bad"} className="dm-cockpit-receipt-chip">{r.kind}</StatusChip>
190
+ <span className="dm-cockpit-receipt-text">{r.detail}</span>
191
+ </li>
192
+ ))}
193
+ </ul>
194
+ </div>
195
+ ) : null}
196
+ </section>
197
+ );
198
+ }
199
+
200
+ export default ApiRegistryCreationCockpit;