@growthub/cli 0.13.4 → 0.13.6

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 (52) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/QUICKSTART.md +19 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/.env.example +8 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/README.md +4 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/action/execute/route.js +60 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/actions/route.js +50 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connect-session/route.js +68 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connection-status/route.js +56 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/proxy/route.js +67 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/status/route.js +50 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +25 -2
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +161 -50
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/NangoConnectionPanel.jsx +496 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +88 -1
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +41 -0
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +120 -17
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/nango/page.jsx +167 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +1 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +67 -11
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +31 -10
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +16 -14
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/env.js +7 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/index.js +38 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-adapter.js +552 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-config-loader.js +202 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-schema.js +303 -0
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +49 -10
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +1 -1
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/nango.js +49 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +4 -2
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +14 -1
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +218 -7
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +28 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +43 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +3 -1
  40. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +36 -0
  41. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +53 -0
  42. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -1
  43. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
  44. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
  45. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
  46. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +102 -3
  47. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
  48. package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +1 -0
  49. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +7 -0
  50. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/project-management.config.json +276 -0
  51. package/dist/index.js +127 -44
  52. package/package.json +1 -1
@@ -0,0 +1,226 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Growthub Workspace Metadata Graph V1 — read-only inspector panel.
5
+ *
6
+ * Surfaces a selected metadata node's dependencies + dependents + warnings.
7
+ *
8
+ * Mounting status (V1):
9
+ * This component SHIPS with the worker kit but is NOT yet mounted by the
10
+ * existing builder / workflow / data-model surfaces. Mounting is
11
+ * intentionally deferred to a follow-up PR so the V1 scope stays focused
12
+ * on the typed projection + GET route. Operators that want the inspector
13
+ * now can import it directly:
14
+ *
15
+ * import { WorkspaceGraphInspectorPanel } from "@/app/data-model/components/WorkspaceGraphInspectorPanel.jsx";
16
+ *
17
+ * Intended future entry points:
18
+ *
19
+ * - Widget sidecar: View dependencies
20
+ * - Workflow sidecar: View dependencies
21
+ * - Run console: View graph lineage
22
+ * - Data Model row sidecar: View dependents
23
+ * - Workspace Settings: Metadata Graph
24
+ *
25
+ * V1 invariants:
26
+ * - Read-only. No edits. No deletes.
27
+ * - No fetch of provider data — only `GET /api/workspace/metadata-graph`.
28
+ * - No secrets rendered.
29
+ */
30
+
31
+ import { useEffect, useMemo, useState } from "react";
32
+ import { findDependencies, findDependents } from "@/lib/workspace-metadata-graph";
33
+
34
+ const RELATION_LABELS = {
35
+ containsWidget: "contains widget",
36
+ bindsToObject: "binds to object",
37
+ usesField: "uses field",
38
+ filteredByField: "filtered by field",
39
+ sortedByField: "sorted by field",
40
+ backedBySourceRecord: "backed by source record",
41
+ scopedToEntity: "scoped to entity",
42
+ containsNode: "contains node",
43
+ usesSandbox: "uses sandbox",
44
+ readsObject: "reads object",
45
+ writesObject: "writes object",
46
+ requiresRunInput: "requires run input",
47
+ callsIntegration: "calls integration",
48
+ usesAgentHost: "uses agent host",
49
+ executedWorkflow: "executed workflow",
50
+ executedSandbox: "executed sandbox",
51
+ usedAgentHost: "used agent host",
52
+ producedArtifact: "produced artifact",
53
+ belongsToIntegration: "belongs to integration",
54
+ boundToIntegration: "bound to integration"
55
+ };
56
+
57
+ const NODE_TYPE_LABELS = {
58
+ dashboard: "Dashboard",
59
+ widget: "Widget",
60
+ dataModelObject: "Data Model object",
61
+ field: "Field",
62
+ view: "View",
63
+ workflow: "Workflow",
64
+ workflowNode: "Workflow node",
65
+ runInput: "Run input",
66
+ sandbox: "Sandbox",
67
+ agentHost: "Agent host",
68
+ integration: "Integration",
69
+ integrationEntity: "Integration entity",
70
+ sourceRecord: "Source record",
71
+ run: "Run",
72
+ outputArtifact: "Output artifact"
73
+ };
74
+
75
+ function relationLabel(relation) {
76
+ return RELATION_LABELS[relation] || relation;
77
+ }
78
+
79
+ function nodeTypeLabel(type) {
80
+ return NODE_TYPE_LABELS[type] || type;
81
+ }
82
+
83
+ export function WorkspaceGraphInspectorPanel({ selectedNodeId, onSelectNode }) {
84
+ const [graph, setGraph] = useState(null);
85
+ const [loading, setLoading] = useState(false);
86
+ const [error, setError] = useState(null);
87
+ const [internalSelectedId, setInternalSelectedId] = useState(selectedNodeId || "");
88
+
89
+ useEffect(() => {
90
+ let canceled = false;
91
+ setLoading(true);
92
+ setError(null);
93
+ fetch("/api/workspace/metadata-graph")
94
+ .then((response) => {
95
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
96
+ return response.json();
97
+ })
98
+ .then((envelope) => {
99
+ if (canceled) return;
100
+ setGraph(envelope?.graph || { nodes: [], edges: [] });
101
+ })
102
+ .catch((err) => {
103
+ if (canceled) return;
104
+ setError(err?.message || "Failed to load metadata graph");
105
+ })
106
+ .finally(() => {
107
+ if (!canceled) setLoading(false);
108
+ });
109
+ return () => { canceled = true; };
110
+ }, []);
111
+
112
+ useEffect(() => {
113
+ if (selectedNodeId && selectedNodeId !== internalSelectedId) {
114
+ setInternalSelectedId(selectedNodeId);
115
+ }
116
+ }, [selectedNodeId, internalSelectedId]);
117
+
118
+ const nodes = graph?.nodes || [];
119
+ const selected = useMemo(
120
+ () => nodes.find((node) => node.id === internalSelectedId) || null,
121
+ [nodes, internalSelectedId]
122
+ );
123
+
124
+ const dependencies = useMemo(
125
+ () => (graph && selected ? findDependencies(graph, selected.id) : []),
126
+ [graph, selected]
127
+ );
128
+ const dependents = useMemo(
129
+ () => (graph && selected ? findDependents(graph, selected.id) : []),
130
+ [graph, selected]
131
+ );
132
+
133
+ const handleSelect = (nodeId) => {
134
+ setInternalSelectedId(nodeId);
135
+ if (typeof onSelectNode === "function") onSelectNode(nodeId);
136
+ };
137
+
138
+ return (
139
+ <div className="workspace-graph-inspector" aria-label="Workspace metadata graph inspector">
140
+ <header className="workspace-graph-inspector-header">
141
+ <h3>Workspace metadata graph</h3>
142
+ <p className="workspace-graph-inspector-subtitle">
143
+ Read-only projection of dashboards, widgets, workflows, sandboxes, and runs.
144
+ </p>
145
+ </header>
146
+
147
+ {loading && <p className="workspace-graph-inspector-loading">Loading metadata graph…</p>}
148
+ {error && (
149
+ <p className="workspace-graph-inspector-error" role="alert">
150
+ Could not load metadata graph: {error}
151
+ </p>
152
+ )}
153
+
154
+ <div className="workspace-graph-inspector-body">
155
+ <aside className="workspace-graph-inspector-list">
156
+ <h4>Nodes ({nodes.length})</h4>
157
+ <ul>
158
+ {nodes.map((node) => (
159
+ <li key={node.id}>
160
+ <button
161
+ type="button"
162
+ className={`workspace-graph-inspector-node-button${node.id === internalSelectedId ? " is-selected" : ""}`}
163
+ onClick={() => handleSelect(node.id)}
164
+ >
165
+ <span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
166
+ <span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
167
+ </button>
168
+ </li>
169
+ ))}
170
+ </ul>
171
+ </aside>
172
+
173
+ <section className="workspace-graph-inspector-detail">
174
+ {!selected && <p className="workspace-graph-inspector-empty">Select a node to view dependencies.</p>}
175
+ {selected && (
176
+ <>
177
+ <h4>{nodeTypeLabel(selected.type)} · {selected.label}</h4>
178
+ <dl className="workspace-graph-inspector-summary">
179
+ {Object.entries(selected.summary || {}).map(([key, value]) => (
180
+ <div key={key}>
181
+ <dt>{key}</dt>
182
+ <dd>{Array.isArray(value) ? value.join(", ") || "—" : (value === null || value === "" ? "—" : String(value))}</dd>
183
+ </div>
184
+ ))}
185
+ </dl>
186
+
187
+ <section className="workspace-graph-inspector-edges">
188
+ <h5>Depends on ({dependencies.length})</h5>
189
+ <ul>
190
+ {dependencies.length === 0 && <li className="workspace-graph-inspector-empty">No dependencies.</li>}
191
+ {dependencies.map(({ node, relation }) => (
192
+ <li key={`dep::${node.id}::${relation}`}>
193
+ <button type="button" onClick={() => handleSelect(node.id)}>
194
+ <span className="workspace-graph-inspector-relation">{relationLabel(relation)}</span>
195
+ <span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
196
+ <span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
197
+ </button>
198
+ </li>
199
+ ))}
200
+ </ul>
201
+ </section>
202
+
203
+ <section className="workspace-graph-inspector-edges">
204
+ <h5>Used by ({dependents.length})</h5>
205
+ <ul>
206
+ {dependents.length === 0 && <li className="workspace-graph-inspector-empty">Nothing depends on this node yet.</li>}
207
+ {dependents.map(({ node, relation }) => (
208
+ <li key={`from::${node.id}::${relation}`}>
209
+ <button type="button" onClick={() => handleSelect(node.id)}>
210
+ <span className="workspace-graph-inspector-relation">{relationLabel(relation)}</span>
211
+ <span className="workspace-graph-inspector-node-type">{nodeTypeLabel(node.type)}</span>
212
+ <span className="workspace-graph-inspector-node-label">{node.label || node.id}</span>
213
+ </button>
214
+ </li>
215
+ ))}
216
+ </ul>
217
+ </section>
218
+ </>
219
+ )}
220
+ </section>
221
+ </div>
222
+ </div>
223
+ );
224
+ }
225
+
226
+ export default WorkspaceGraphInspectorPanel;
@@ -396,6 +396,7 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
396
396
  }
397
397
 
398
398
  .workspace-builder {
399
+ --workspace-rail-width: 264px;
399
400
  min-height: 100vh;
400
401
  display: grid;
401
402
  grid-template-columns: 264px minmax(0, 1fr) 320px;
@@ -427,6 +428,7 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
427
428
  overflow: hidden;
428
429
  }
429
430
  body.workspace-rail-collapsed .workspace-builder {
431
+ --workspace-rail-width: 52px;
430
432
  grid-template-columns: 52px minmax(0, 1fr) 320px;
431
433
  }
432
434
  body.workspace-rail-collapsed .workspace-builder.workspace-settings-page {
@@ -456,24 +458,19 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
456
458
  .workspace-rail.is-collapsed .workspace-rail-topbar {
457
459
  justify-content: center;
458
460
  width: 100%;
461
+ display: contents;
459
462
  }
460
463
  .workspace-rail.is-collapsed .workspace-rail-topbar-actions {
461
- display: flex;
462
- flex-direction: column;
463
- justify-content: center;
464
- gap: 8px;
465
- }
466
- .workspace-rail.is-collapsed .workspace-rail-topbar-actions .workspace-rail-icon-btn {
467
- order: 2;
464
+ display: contents;
468
465
  }
469
466
  .workspace-rail.is-collapsed .workspace-rail-icon-btn[aria-pressed="true"] {
470
467
  order: 1;
471
468
  }
472
469
  .workspace-rail.is-collapsed .workspace-rail-icon-btn[data-rail-search] {
473
- order: 2;
470
+ order: 3;
474
471
  }
475
472
  .workspace-rail.is-collapsed .workspace-rail-icon-btn[aria-label="Workspace settings"] {
476
- order: 3;
473
+ order: 4;
477
474
  }
478
475
  .workspace-rail.is-collapsed .workspace-rail-icon-btn[aria-pressed="true"] svg {
479
476
  transform: rotate(180deg);
@@ -482,6 +479,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
482
479
  display: flex;
483
480
  justify-content: center;
484
481
  width: 100%;
482
+ order: 2;
485
483
  }
486
484
  .workspace-rail.is-collapsed .workspace-rail-tabs {
487
485
  flex-direction: column;
@@ -2128,6 +2126,63 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
2128
2126
  transition: opacity .12s ease;
2129
2127
  }
2130
2128
 
2129
+ .workspace-dashboard-surface {
2130
+ background: #f7f7f8;
2131
+ padding: 0 32px 24px;
2132
+ }
2133
+ .workspace-dashboard-surface .dm-workflow-toolbar {
2134
+ margin: 0 0 18px;
2135
+ padding: 0;
2136
+ background: #ffffff;
2137
+ border-bottom-color: #e8e8e8;
2138
+ }
2139
+ .workspace-dashboard-surface .workspace-canvas {
2140
+ width: 100%;
2141
+ margin: 0 auto;
2142
+ border: 1px solid #e5e7eb;
2143
+ border-radius: 8px;
2144
+ background: #f7f7f8;
2145
+ max-height: calc(100vh - 72px);
2146
+ }
2147
+ .workspace-dashboard-surface .workspace-tabs {
2148
+ gap: 6px;
2149
+ min-height: 44px;
2150
+ padding: 8px 12px 12px;
2151
+ background: #ffffff;
2152
+ border-bottom: 1px solid #ececec;
2153
+ border-radius: 8px 8px 0 0;
2154
+ }
2155
+ .workspace-dashboard-surface .workspace-tabs button {
2156
+ min-height: 30px;
2157
+ }
2158
+ .workspace-dashboard-surface .workspace-grid {
2159
+ gap: 12px;
2160
+ padding: 18px;
2161
+ }
2162
+ .workspace-dashboard-surface .workspace-grid-cell {
2163
+ background: #ffffff;
2164
+ border-color: #e8e8e8;
2165
+ }
2166
+ .workspace-dashboard-surface.is-dashboard-editing {
2167
+ padding-inline: 16px;
2168
+ }
2169
+ .workspace-dashboard-surface.is-dashboard-editing .workspace-canvas {
2170
+ max-height: calc(100vh - 64px);
2171
+ }
2172
+ .workspace-dashboard-surface.is-dashboard-editing .workspace-grid {
2173
+ gap: 10px;
2174
+ padding: 14px;
2175
+ }
2176
+
2177
+ @media (max-width: 900px) {
2178
+ .workspace-dashboard-surface {
2179
+ padding-inline: 20px;
2180
+ }
2181
+ .workspace-dashboard-surface .dm-workflow-toolbar {
2182
+ margin-inline: 0;
2183
+ }
2184
+ }
2185
+
2131
2186
  @media (max-width: 1080px) {
2132
2187
  .workspace-builder {
2133
2188
  grid-template-columns: 180px minmax(0, 1fr);
@@ -4890,32 +4945,49 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4890
4945
  background: #f7f7f8;
4891
4946
  overflow: hidden;
4892
4947
  }
4948
+ .workspace-surface.workspace-dashboard-surface {
4949
+ background: #f7f7f8;
4950
+ padding: 0 32px 24px;
4951
+ }
4952
+ .workspace-surface.workspace-dashboard-surface.is-dashboard-editing {
4953
+ padding-inline: 16px;
4954
+ }
4893
4955
  .dm-workflow-toolbar {
4894
4956
  height: 40px;
4895
4957
  min-height: 40px;
4896
4958
  align-items: center;
4897
4959
  flex-wrap: nowrap;
4898
4960
  gap: 12px;
4899
- padding: 0 12px;
4900
- margin: 0;
4961
+ padding: 0 32px;
4962
+ margin: 0 -32px 18px;
4901
4963
  border-bottom: 1px solid #ececef;
4902
4964
  background: #fbfbfc;
4903
4965
  overflow: hidden;
4904
4966
  }
4967
+ .workspace-surface.workspace-dashboard-surface .dm-workflow-toolbar {
4968
+ padding-inline: 32px;
4969
+ margin-inline: -32px;
4970
+ }
4971
+ .workspace-dashboard-surface.is-dashboard-editing .dm-workflow-toolbar {
4972
+ padding-inline: 16px;
4973
+ margin-inline: -16px;
4974
+ }
4905
4975
  .dm-workflow-titlebar {
4906
4976
  display: flex;
4907
4977
  align-items: center;
4908
4978
  gap: 6px;
4909
- min-width: 0;
4910
- flex: 1;
4979
+ min-width: max-content;
4980
+ flex: 1 0 auto;
4911
4981
  color: #8b8b91;
4912
4982
  font-size: 13px;
4913
4983
  font-weight: 500;
4984
+ white-space: nowrap;
4914
4985
  }
4915
4986
  .dm-workflow-titlebar h1 {
4916
4987
  margin: 0;
4917
- min-width: 0;
4918
- max-width: 260px;
4988
+ min-width: max-content;
4989
+ max-width: none;
4990
+ flex: 0 0 auto;
4919
4991
  overflow: hidden;
4920
4992
  text-overflow: ellipsis;
4921
4993
  white-space: nowrap;
@@ -4924,6 +4996,20 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4924
4996
  font-weight: 600;
4925
4997
  letter-spacing: 0;
4926
4998
  }
4999
+ .dm-workflow-breadcrumb-link {
5000
+ appearance: none;
5001
+ border: 0;
5002
+ background: transparent;
5003
+ padding: 0;
5004
+ color: #a1a1aa;
5005
+ font: inherit;
5006
+ font-size: 13px;
5007
+ font-weight: 600;
5008
+ cursor: pointer;
5009
+ }
5010
+ .dm-workflow-breadcrumb-link:hover {
5011
+ color: #4b4b52;
5012
+ }
4927
5013
  .dm-workflow-title-icon {
4928
5014
  display: grid;
4929
5015
  place-items: center;
@@ -4943,8 +5029,9 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4943
5029
  display: flex;
4944
5030
  align-items: center;
4945
5031
  gap: 6px;
4946
- flex-shrink: 0;
4947
- max-width: 66vw;
5032
+ flex: 0 1 auto;
5033
+ min-width: 0;
5034
+ max-width: 58vw;
4948
5035
  overflow-x: auto;
4949
5036
  scrollbar-width: none;
4950
5037
  }
@@ -8398,3 +8485,19 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
8398
8485
  .dm-run-setup__actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
8399
8486
  .dm-run-setup__actions .dm-btn-outline,
8400
8487
  .dm-run-setup__actions .dm-workflow-chip-btn { display: inline-flex; align-items: center; gap: 6px; }
8488
+
8489
+ /* Agent swarm — sidecar editor (re-uses dm-orchestration-config tokens) */
8490
+ .dm-agent-swarm-panel__row { display: flex; align-items: center; gap: 6px; }
8491
+ .dm-agent-swarm-panel__row > span:first-child { flex: 1; font-size: 11px; font-weight: 600; color: #6b7280; text-transform: uppercase; letter-spacing: 0.04em; }
8492
+ .dm-agent-swarm-panel__row .dm-btn-outline { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; padding: 3px 7px; }
8493
+ .dm-agent-swarm-panel__subagent { border: 1px solid #edf0f3; border-radius: 4px; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
8494
+ .dm-agent-swarm-panel__role { flex: 1; border: 1px solid #d1d5db; border-radius: 4px; padding: 4px 6px; font-size: 12px; color: #111827; background: #fff; }
8495
+ .dm-agent-swarm-panel__weights { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
8496
+
8497
+ /* Agent swarm — condensed run trace summary */
8498
+ .dm-swarm-summary__line { display: flex; flex-wrap: wrap; gap: 6px 12px; font-size: 11px; color: #6b7280; align-items: center; margin: 0 0 6px; }
8499
+ .dm-swarm-summary__line strong { color: #111827; font-weight: 600; }
8500
+ .dm-swarm-summary__kind { font-size: 10px; text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; border: 1px solid #e5e7eb; border-radius: 999px; padding: 1px 6px; }
8501
+ .dm-swarm-phase { border: 1px solid #edf0f3; border-radius: 4px; padding: 6px 8px; font-size: 11px; }
8502
+ .dm-swarm-phase summary { cursor: pointer; color: #6b7280; font-weight: 500; }
8503
+ .dm-swarm-phase pre { margin: 6px 0 0; font-size: 11px; white-space: pre-wrap; color: #111827; max-height: 240px; overflow: auto; }
@@ -0,0 +1,167 @@
1
+ import Link from "next/link";
2
+ import { Suspense } from "react";
3
+ import { describeNangoAdapter, getStatus } from "@/lib/adapters/integrations/nango";
4
+ import { readWorkspaceConfig } from "@/lib/workspace-config";
5
+ import { WorkspaceRail } from "../../../workspace-rail.jsx";
6
+
7
+ /**
8
+ * Nango integration settings — inspect-only panel.
9
+ *
10
+ * Reads from the EXISTING `objectType: "api-registry"` rows in
11
+ * `dataModel.objects[]`. Rows that declare `connectorKind: "nango"` are
12
+ * Nango-backed; other connectorKinds (http, mcp, chrome, tool) are shown
13
+ * here only for context — they keep going through their own resolvers.
14
+ *
15
+ * Secrets are not displayed. The Nango secret lives in env (default env-ref
16
+ * name: NANGO_SECRET_KEY); the row's `authRef` column names the env-ref.
17
+ */
18
+
19
+ function listApiRegistryObjects(workspaceConfig) {
20
+ const objects = workspaceConfig?.dataModel?.objects;
21
+ if (!Array.isArray(objects)) return [];
22
+ return objects.filter((object) => object?.objectType === "api-registry");
23
+ }
24
+
25
+ function flattenNangoRows(apiRegistryObjects) {
26
+ const rows = [];
27
+ for (const object of apiRegistryObjects) {
28
+ const objectRows = Array.isArray(object.rows) ? object.rows : [];
29
+ for (const row of objectRows) {
30
+ if (row?.connectorKind === "nango") {
31
+ rows.push({ object, row });
32
+ }
33
+ }
34
+ }
35
+ return rows;
36
+ }
37
+
38
+ function summarizeList(value) {
39
+ if (Array.isArray(value)) return value.filter(Boolean);
40
+ if (typeof value === "string" && value.trim()) {
41
+ return value.split(",").map((v) => v.trim()).filter(Boolean);
42
+ }
43
+ return [];
44
+ }
45
+
46
+ function NangoRowCard({ object, row }) {
47
+ const connectionIds = summarizeList(row.connectionIds);
48
+ const enabledActions = summarizeList(row.enabledActions);
49
+ const providerConfigKey = row.providerConfigKey || row.integrationId;
50
+ return <article className="workspace-integration-row">
51
+ <div className="workspace-provider-mark">{String(row.integrationId || "?").slice(0, 1).toUpperCase()}</div>
52
+ <div className="workspace-integration-main">
53
+ <strong>{row.integrationId}</strong>
54
+ <p>providerConfigKey: <code>{providerConfigKey || "—"}</code></p>
55
+ <div className="workspace-integration-meta">
56
+ <span>object: <code>{object.id}</code></span>
57
+ <span>{row.nangoMode || "cloud"}</span>
58
+ <span>env: {row.nangoEnvironment || "dev"}</span>
59
+ <span>{connectionIds.length} {connectionIds.length === 1 ? "connection" : "connections"}</span>
60
+ <span>{enabledActions.length} {enabledActions.length === 1 ? "action" : "actions"}</span>
61
+ {row.authRef ? <span><code>{row.authRef}</code></span> : null}
62
+ {row.endpoint ? <span>endpoint: <code>{row.endpoint}</code></span> : null}
63
+ </div>
64
+ </div>
65
+ <span className={`workspace-integration-status ${row.status || "configured"}`}>{row.status || "configured"}</span>
66
+ </article>;
67
+ }
68
+
69
+ async function NangoIntegrationsSettingsPage() {
70
+ const adapter = describeNangoAdapter();
71
+ const workspaceConfig = await readWorkspaceConfig();
72
+ const apiRegistryObjects = listApiRegistryObjects(workspaceConfig);
73
+ const nangoRows = flattenNangoRows(apiRegistryObjects);
74
+
75
+ let status;
76
+ try {
77
+ status = await getStatus();
78
+ } catch (error) {
79
+ status = { status: "disconnected", reason: error?.message || "status probe failed" };
80
+ }
81
+
82
+ return <main className="workspace-builder workspace-settings-page">
83
+ <Suspense fallback={null}>
84
+ <WorkspaceRail
85
+ workspaceConfig={workspaceConfig}
86
+ authority={adapter.authority}
87
+ managementSlot={(
88
+ <Link className="active" href="/settings/integrations">Integrations</Link>
89
+ )}
90
+ />
91
+ </Suspense>
92
+
93
+ <section className="workspace-surface">
94
+ <header className="workspace-toolbar">
95
+ <div>
96
+ <p>Workspace settings &middot; Integrations</p>
97
+ <h1>Nango</h1>
98
+ </div>
99
+ <div className="workspace-toolbar-actions">
100
+ <Link href="/settings/integrations">All integrations</Link>
101
+ <span>{adapter.id}</span>
102
+ <span>{adapter.authority}</span>
103
+ </div>
104
+ </header>
105
+
106
+ <section className="workspace-integration-summary" aria-label="Nango adapter summary">
107
+ <article>
108
+ <span>Adapter</span>
109
+ <strong>{adapter.label}</strong>
110
+ <div>
111
+ <code>{adapter.secretEnvName}</code>
112
+ <span> &middot; row-scoped via <code>connectorKind: "nango"</code></span>
113
+ </div>
114
+ </article>
115
+ <article>
116
+ <span>Mode</span>
117
+ <strong>{adapter.mode}</strong>
118
+ <div>{adapter.hostUrl ? <code>{adapter.hostUrl}</code> : <code>nango cloud</code>}</div>
119
+ </article>
120
+ <article>
121
+ <span>Status</span>
122
+ <strong>{status.status}</strong>
123
+ <div>{status.reason ? <span>{status.reason}</span> : <span>environment: <code>{status.environment || adapter.environment}</code></span>}</div>
124
+ </article>
125
+ </section>
126
+
127
+ <section className="workspace-integration-toolbar">
128
+ <div>
129
+ <strong>Nango-backed API Registry rows</strong>
130
+ <p>Rows in <code>dataModel.objects[]</code> with <code>objectType: "api-registry"</code> and <code>connectorKind: "nango"</code>. Edit <code>growthub.config.json</code> to add or remove rows — secrets stay in env, referenced by the row's <code>authRef</code>.</p>
131
+ </div>
132
+ </section>
133
+
134
+ <section className="workspace-integration-board">
135
+ {nangoRows.length === 0
136
+ ? <article className="workspace-integration-section">
137
+ <div className="workspace-integration-section-heading">
138
+ <div>
139
+ <h2>No Nango-backed rows yet</h2>
140
+ <p>Add a row to an <code>api-registry</code> object with <code>connectorKind: "nango"</code>, an <code>integrationId</code>, and an optional <code>providerConfigKey</code>. Apply the <code>nango</code> resolver template from the Data Model template picker for sensible defaults.</p>
141
+ </div>
142
+ </div>
143
+ </article>
144
+ : <article className="workspace-integration-section">
145
+ <div className="workspace-integration-section-heading">
146
+ <div>
147
+ <h2>Nango providers</h2>
148
+ <p>{nangoRows.length} row{nangoRows.length === 1 ? "" : "s"} across {apiRegistryObjects.length} API Registry object{apiRegistryObjects.length === 1 ? "" : "s"}.</p>
149
+ </div>
150
+ <span>{nangoRows.length}</span>
151
+ </div>
152
+ <div className="workspace-integration-list">
153
+ {nangoRows.map(({ object, row }, index) => <NangoRowCard
154
+ object={object}
155
+ row={row}
156
+ key={`${object.id}:${row.integrationId || index}`}
157
+ />)}
158
+ </div>
159
+ </article>}
160
+ </section>
161
+ </section>
162
+ </main>;
163
+ }
164
+
165
+ export {
166
+ NangoIntegrationsSettingsPage as default
167
+ };
@@ -77,6 +77,7 @@ async function IntegrationsSettingsPage() {
77
77
  </div>
78
78
  <div className="workspace-toolbar-actions">
79
79
  <Link href="/api/settings/integrations">API contract</Link>
80
+ <Link href="/settings/integrations/nango">Nango</Link>
80
81
  <span>{adapter.id}</span>
81
82
  <span>{adapter.authority}</span>
82
83
  </div>