@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.
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/QUICKSTART.md +19 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/.env.example +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/README.md +4 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/action/execute/route.js +60 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/actions/route.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connect-session/route.js +68 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connection-status/route.js +56 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/proxy/route.js +67 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/status/route.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +25 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +161 -50
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/NangoConnectionPanel.jsx +496 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +88 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +120 -17
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/nango/page.jsx +167 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +67 -11
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +31 -10
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +16 -14
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/env.js +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/index.js +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-adapter.js +552 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-config-loader.js +202 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-schema.js +303 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +49 -10
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/nango.js +49 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +4 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +14 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +218 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +28 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +43 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +3 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +36 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +53 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +102 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/project-management.config.json +276 -0
- package/dist/index.js +127 -44
- 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;
|
package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css
CHANGED
|
@@ -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:
|
|
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:
|
|
470
|
+
order: 3;
|
|
474
471
|
}
|
|
475
472
|
.workspace-rail.is-collapsed .workspace-rail-icon-btn[aria-label="Workspace settings"] {
|
|
476
|
-
order:
|
|
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
|
|
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:
|
|
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:
|
|
4918
|
-
max-width:
|
|
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
|
|
4947
|
-
|
|
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 · 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> · 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>
|