@growthub/cli 0.13.0 → 0.13.2
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/apps/workspace/app/api/workspace/sandbox-run/route.js +50 -25
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryActionCard.jsx +141 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryReviewModal.jsx +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +522 -35
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +242 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +52 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +1203 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +163 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxOrchestrationEditorPanel.jsx +190 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolConfirmModal.jsx +64 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolDraftPanel.jsx +376 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +6 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +1062 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +10 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +906 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/page.jsx +12 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +492 -28
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +114 -30
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/nav-workflows.js +54 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +322 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +734 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +73 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-sidecar-routing.js +24 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +21 -1
- package/package.json +1 -1
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { ArrowLeft } from "lucide-react";
|
|
5
|
+
import {
|
|
6
|
+
addCanonicalNodeToGraph,
|
|
7
|
+
buildBlankOrchestrationGraphShell,
|
|
8
|
+
buildDefaultOrchestrationGraphFromRegistry,
|
|
9
|
+
getNextCanonicalNodeId,
|
|
10
|
+
isApiRegistryTestSuccessful,
|
|
11
|
+
getOrchestrationGraphUiState,
|
|
12
|
+
parseOrchestrationGraph,
|
|
13
|
+
serializeOrchestrationGraph,
|
|
14
|
+
updateGraphNode,
|
|
15
|
+
validateOrchestrationGraph
|
|
16
|
+
} from "@/lib/orchestration-graph";
|
|
17
|
+
import { resolveConnectorAction } from "@/lib/orchestration-sidecar-routing";
|
|
18
|
+
import { OrchestrationGraphCanvas } from "./OrchestrationGraphCanvas.jsx";
|
|
19
|
+
import { OrchestrationGraphEmptyCanvas } from "./OrchestrationGraphEmptyCanvas.jsx";
|
|
20
|
+
import { OrchestrationNodeConfigPanel } from "./OrchestrationNodeConfigPanel.jsx";
|
|
21
|
+
|
|
22
|
+
export function SandboxToolDraftPanel({
|
|
23
|
+
registryRow,
|
|
24
|
+
draftOptions,
|
|
25
|
+
onDraftChange,
|
|
26
|
+
onRequestConfirm,
|
|
27
|
+
onCancel,
|
|
28
|
+
disabled
|
|
29
|
+
}) {
|
|
30
|
+
const integrationId = String(registryRow?.integrationId || "").trim();
|
|
31
|
+
const registryName = String(registryRow?.Name || integrationId).trim();
|
|
32
|
+
const defaultName = registryRow?.Name
|
|
33
|
+
? `${String(registryRow.Name).trim()} Tool`
|
|
34
|
+
: `${integrationId} Tool`;
|
|
35
|
+
|
|
36
|
+
const [name, setName] = useState(draftOptions?.name || defaultName);
|
|
37
|
+
const [description, setDescription] = useState(draftOptions?.description || String(registryRow?.description || "").trim());
|
|
38
|
+
const [runLocality, setRunLocality] = useState(draftOptions?.runLocality || "local");
|
|
39
|
+
const [adapter, setAdapter] = useState(draftOptions?.adapter || "local-process");
|
|
40
|
+
const [authRef, setAuthRef] = useState(draftOptions?.authRef || String(registryRow?.authRef || integrationId).trim());
|
|
41
|
+
const [envRefs, setEnvRefs] = useState(draftOptions?.envRefs || "");
|
|
42
|
+
const [networkAllow, setNetworkAllow] = useState(Boolean(draftOptions?.networkAllow));
|
|
43
|
+
const [timeoutMs, setTimeoutMs] = useState(String(draftOptions?.timeoutMs || "30000"));
|
|
44
|
+
const [rootPath, setRootPath] = useState(draftOptions?.rootPath || "data");
|
|
45
|
+
const [instructions, setInstructions] = useState(draftOptions?.instructions || "");
|
|
46
|
+
const [agentHost, setAgentHost] = useState(draftOptions?.agentHost || "");
|
|
47
|
+
const [schedulerRegistryId, setSchedulerRegistryId] = useState(
|
|
48
|
+
draftOptions?.schedulerRegistryId || (draftOptions?.runLocality === "serverless" ? integrationId : "")
|
|
49
|
+
);
|
|
50
|
+
const [selectedNodeId, setSelectedNodeId] = useState("input");
|
|
51
|
+
const [configTab, setConfigTab] = useState("node");
|
|
52
|
+
const [graphError, setGraphError] = useState("");
|
|
53
|
+
const [orchestrationGraph, setOrchestrationGraph] = useState(() => {
|
|
54
|
+
if (draftOptions?.orchestrationGraph) {
|
|
55
|
+
return parseOrchestrationGraph(draftOptions.orchestrationGraph);
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const registryKey = `${integrationId}:${String(registryRow?.endpoint || "")}:${String(registryRow?.method || "")}`;
|
|
61
|
+
const graphUiState = getOrchestrationGraphUiState(orchestrationGraph);
|
|
62
|
+
const graphUnset = graphUiState === "unset";
|
|
63
|
+
const graphBlankShell = graphUiState === "blank-shell";
|
|
64
|
+
const nextNodeId = useMemo(
|
|
65
|
+
() => (orchestrationGraph ? getNextCanonicalNodeId(orchestrationGraph) : "input"),
|
|
66
|
+
[orchestrationGraph]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (graphUnset || graphBlankShell) return;
|
|
71
|
+
setOrchestrationGraph((current) => {
|
|
72
|
+
const base = buildDefaultOrchestrationGraphFromRegistry(registryRow, {
|
|
73
|
+
label: registryName,
|
|
74
|
+
authRef,
|
|
75
|
+
rootPath
|
|
76
|
+
});
|
|
77
|
+
const parsed = parseOrchestrationGraph(current) || current;
|
|
78
|
+
if (!parsed?.nodes?.length) return current;
|
|
79
|
+
return {
|
|
80
|
+
...parsed,
|
|
81
|
+
nodes: parsed.nodes.map((node) => {
|
|
82
|
+
const template = base.nodes.find((n) => n.id === node.id);
|
|
83
|
+
if (!template) return node;
|
|
84
|
+
if (node.id === "api-request") {
|
|
85
|
+
return {
|
|
86
|
+
...node,
|
|
87
|
+
subtitle: template.subtitle,
|
|
88
|
+
config: { ...template.config, ...node.config, authRef }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (node.id === "transform") {
|
|
92
|
+
return { ...node, config: { ...node.config, rootPath } };
|
|
93
|
+
}
|
|
94
|
+
return node;
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
}, [registryKey, registryName, authRef, rootPath, registryRow, integrationId, graphUnset, graphBlankShell]);
|
|
99
|
+
|
|
100
|
+
const selectedNode = useMemo(() => {
|
|
101
|
+
const parsed = parseOrchestrationGraph(orchestrationGraph) || orchestrationGraph;
|
|
102
|
+
if (!selectedNodeId || !parsed?.nodes) return null;
|
|
103
|
+
return parsed.nodes.find((n) => String(n.id) === selectedNodeId) || null;
|
|
104
|
+
}, [orchestrationGraph, selectedNodeId]);
|
|
105
|
+
|
|
106
|
+
const graphSerialized = useMemo(
|
|
107
|
+
() => (graphUnset ? "" : serializeOrchestrationGraph(orchestrationGraph)),
|
|
108
|
+
[orchestrationGraph, graphUnset]
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (graphUnset || graphBlankShell) {
|
|
113
|
+
setGraphError(graphBlankShell ? "Add at least Input and API Registry nodes before creating." : "");
|
|
114
|
+
} else {
|
|
115
|
+
const validation = validateOrchestrationGraph(orchestrationGraph);
|
|
116
|
+
setGraphError(validation.ok ? "" : validation.errors[0] || "Invalid graph");
|
|
117
|
+
}
|
|
118
|
+
onDraftChange?.({
|
|
119
|
+
name,
|
|
120
|
+
description,
|
|
121
|
+
runLocality,
|
|
122
|
+
adapter,
|
|
123
|
+
authRef,
|
|
124
|
+
envRefs,
|
|
125
|
+
networkAllow,
|
|
126
|
+
timeoutMs,
|
|
127
|
+
rootPath,
|
|
128
|
+
instructions,
|
|
129
|
+
agentHost,
|
|
130
|
+
schedulerRegistryId,
|
|
131
|
+
orchestrationGraph: graphSerialized
|
|
132
|
+
});
|
|
133
|
+
}, [
|
|
134
|
+
name,
|
|
135
|
+
description,
|
|
136
|
+
runLocality,
|
|
137
|
+
adapter,
|
|
138
|
+
authRef,
|
|
139
|
+
envRefs,
|
|
140
|
+
networkAllow,
|
|
141
|
+
timeoutMs,
|
|
142
|
+
rootPath,
|
|
143
|
+
instructions,
|
|
144
|
+
agentHost,
|
|
145
|
+
schedulerRegistryId,
|
|
146
|
+
graphSerialized,
|
|
147
|
+
orchestrationGraph,
|
|
148
|
+
graphUnset,
|
|
149
|
+
graphBlankShell,
|
|
150
|
+
onDraftChange
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
function startFromRegistry() {
|
|
154
|
+
setOrchestrationGraph(buildDefaultOrchestrationGraphFromRegistry(registryRow, { authRef, rootPath }));
|
|
155
|
+
setSelectedNodeId("input");
|
|
156
|
+
setConfigTab("node");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function startBlank() {
|
|
160
|
+
setOrchestrationGraph(buildBlankOrchestrationGraphShell());
|
|
161
|
+
setSelectedNodeId("input");
|
|
162
|
+
setConfigTab("node");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function applyPastedGraph(text) {
|
|
166
|
+
const parsed = parseOrchestrationGraph(text);
|
|
167
|
+
if (parsed) setOrchestrationGraph(parsed);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function addNextNode() {
|
|
171
|
+
if (!nextNodeId) return;
|
|
172
|
+
setOrchestrationGraph((g) => addCanonicalNodeToGraph(
|
|
173
|
+
g || buildBlankOrchestrationGraphShell(),
|
|
174
|
+
nextNodeId,
|
|
175
|
+
registryRow,
|
|
176
|
+
{ authRef, rootPath }
|
|
177
|
+
));
|
|
178
|
+
setSelectedNodeId(nextNodeId);
|
|
179
|
+
setConfigTab("node");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function handleNodeConfigChange(configPatch) {
|
|
183
|
+
if (!selectedNodeId) return;
|
|
184
|
+
setOrchestrationGraph((g) => updateGraphNode(g, selectedNodeId, configPatch));
|
|
185
|
+
if (selectedNodeId === "transform" && configPatch.rootPath) {
|
|
186
|
+
setRootPath(String(configPatch.rootPath));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function handleConnectorAction(payload) {
|
|
191
|
+
const { nodeId, tab } = resolveConnectorAction(payload);
|
|
192
|
+
setSelectedNodeId(nodeId);
|
|
193
|
+
setConfigTab(tab);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const defaultInstructions = `Governed sandbox tool for ${registryName}. Calls ${String(registryRow?.method || "GET").toUpperCase()} ${registryRow?.endpoint || registryRow?.baseUrl || ""}. authRef ${authRef} only — secrets resolve server-side.`;
|
|
197
|
+
const headerBadge = isApiRegistryTestSuccessful(registryRow) ? "connected" : "draft";
|
|
198
|
+
const canCreate = graphUiState === "populated" && !graphError && isApiRegistryTestSuccessful(registryRow);
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<section className="dm-orchestration-sidecar" aria-label="Sandbox orchestration field editor">
|
|
202
|
+
<header className="dm-orchestration-header">
|
|
203
|
+
<button type="button" className="dm-orchestration-header__back" onClick={onCancel} aria-label="Back">
|
|
204
|
+
<ArrowLeft size={16} />
|
|
205
|
+
</button>
|
|
206
|
+
<div className="dm-orchestration-header__titles">
|
|
207
|
+
<h2>Sandbox tool draft</h2>
|
|
208
|
+
<p>Created from {registryName}</p>
|
|
209
|
+
</div>
|
|
210
|
+
<span className={`dm-orchestration-header__badge is-${headerBadge}`}>{headerBadge}</span>
|
|
211
|
+
<div className="dm-orchestration-header__actions">
|
|
212
|
+
<button type="button" className="dm-btn-outline" disabled={disabled} onClick={onCancel}>
|
|
213
|
+
Cancel
|
|
214
|
+
</button>
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
className="dm-btn-primary-sm"
|
|
218
|
+
disabled={disabled || !name.trim() || !canCreate}
|
|
219
|
+
onClick={onRequestConfirm}
|
|
220
|
+
>
|
|
221
|
+
Create tool
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
</header>
|
|
225
|
+
|
|
226
|
+
<div className="dm-orchestration-sidecar__body">
|
|
227
|
+
<div className="dm-orchestration-sidecar__canvas-col">
|
|
228
|
+
{graphUnset ? (
|
|
229
|
+
<OrchestrationGraphEmptyCanvas
|
|
230
|
+
disabled={disabled}
|
|
231
|
+
onStartFromRegistry={startFromRegistry}
|
|
232
|
+
onStartBlank={startBlank}
|
|
233
|
+
onPasteGraph={applyPastedGraph}
|
|
234
|
+
/>
|
|
235
|
+
) : graphBlankShell ? (
|
|
236
|
+
<div className="dm-orchestration-canvas dm-orchestration-canvas--blank-shell">
|
|
237
|
+
<p className="dm-orchestration-canvas__blank-hint">Add first node</p>
|
|
238
|
+
<button type="button" className="dm-btn-outline" disabled={disabled} onClick={addNextNode}>
|
|
239
|
+
+ Add Input
|
|
240
|
+
</button>
|
|
241
|
+
</div>
|
|
242
|
+
) : (
|
|
243
|
+
<>
|
|
244
|
+
<OrchestrationGraphCanvas
|
|
245
|
+
graph={orchestrationGraph}
|
|
246
|
+
selectedNodeId={selectedNodeId}
|
|
247
|
+
onSelectNode={(node) => {
|
|
248
|
+
setSelectedNodeId(String(node?.id || ""));
|
|
249
|
+
setConfigTab("node");
|
|
250
|
+
}}
|
|
251
|
+
onConnectorAction={handleConnectorAction}
|
|
252
|
+
/>
|
|
253
|
+
{nextNodeId && (
|
|
254
|
+
<button
|
|
255
|
+
type="button"
|
|
256
|
+
className="dm-btn-outline dm-orchestration-canvas__add-node"
|
|
257
|
+
disabled={disabled}
|
|
258
|
+
onClick={addNextNode}
|
|
259
|
+
>
|
|
260
|
+
+ Add {nextNodeId === "api-request" ? "API Registry" : nextNodeId === "transform" ? "Transform" : nextNodeId === "result" ? "Result" : "Input"}
|
|
261
|
+
</button>
|
|
262
|
+
)}
|
|
263
|
+
</>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div className="dm-orchestration-sidecar__config-col">
|
|
268
|
+
{graphUiState === "populated" && (
|
|
269
|
+
<OrchestrationNodeConfigPanel
|
|
270
|
+
node={selectedNode}
|
|
271
|
+
registryRow={registryRow}
|
|
272
|
+
disabled={disabled}
|
|
273
|
+
activeTab={configTab}
|
|
274
|
+
onTabChange={setConfigTab}
|
|
275
|
+
onConfigChange={handleNodeConfigChange}
|
|
276
|
+
/>
|
|
277
|
+
)}
|
|
278
|
+
|
|
279
|
+
<details className="dm-orchestration-runtime">
|
|
280
|
+
<summary>Runtime (sandbox row)</summary>
|
|
281
|
+
<div className="dm-orchestration-runtime__fields">
|
|
282
|
+
<label className="dm-orchestration-config__field">
|
|
283
|
+
<span>Name</span>
|
|
284
|
+
<input value={name} disabled={disabled} onChange={(e) => setName(e.target.value)} />
|
|
285
|
+
</label>
|
|
286
|
+
<label className="dm-orchestration-config__field">
|
|
287
|
+
<span>Description</span>
|
|
288
|
+
<textarea rows={2} value={description} disabled={disabled} onChange={(e) => setDescription(e.target.value)} />
|
|
289
|
+
</label>
|
|
290
|
+
<label className="dm-orchestration-config__field">
|
|
291
|
+
<span>Run locality</span>
|
|
292
|
+
<select
|
|
293
|
+
value={runLocality}
|
|
294
|
+
disabled={disabled}
|
|
295
|
+
onChange={(e) => {
|
|
296
|
+
const next = e.target.value;
|
|
297
|
+
setRunLocality(next);
|
|
298
|
+
setAdapter(next === "serverless" ? "serverless" : "local-process");
|
|
299
|
+
if (next === "serverless" && !schedulerRegistryId) {
|
|
300
|
+
setSchedulerRegistryId(integrationId);
|
|
301
|
+
}
|
|
302
|
+
}}
|
|
303
|
+
>
|
|
304
|
+
<option value="local">local</option>
|
|
305
|
+
<option value="serverless">serverless</option>
|
|
306
|
+
</select>
|
|
307
|
+
</label>
|
|
308
|
+
<label className="dm-orchestration-config__field">
|
|
309
|
+
<span>Adapter</span>
|
|
310
|
+
<select value={adapter} disabled={disabled} onChange={(e) => setAdapter(e.target.value)}>
|
|
311
|
+
<option value="local-process">local-process</option>
|
|
312
|
+
<option value="local-agent-host">local-agent-host</option>
|
|
313
|
+
<option value="serverless">serverless</option>
|
|
314
|
+
</select>
|
|
315
|
+
</label>
|
|
316
|
+
{adapter === "local-agent-host" && (
|
|
317
|
+
<label className="dm-orchestration-config__field">
|
|
318
|
+
<span>Agent host</span>
|
|
319
|
+
<input value={agentHost} disabled={disabled} onChange={(e) => setAgentHost(e.target.value)} />
|
|
320
|
+
</label>
|
|
321
|
+
)}
|
|
322
|
+
{runLocality === "serverless" && (
|
|
323
|
+
<label className="dm-orchestration-config__field">
|
|
324
|
+
<span>Scheduler registry ID</span>
|
|
325
|
+
<input
|
|
326
|
+
value={schedulerRegistryId}
|
|
327
|
+
disabled={disabled}
|
|
328
|
+
onChange={(e) => setSchedulerRegistryId(e.target.value)}
|
|
329
|
+
/>
|
|
330
|
+
</label>
|
|
331
|
+
)}
|
|
332
|
+
<label className="dm-orchestration-config__field">
|
|
333
|
+
<span>Auth reference</span>
|
|
334
|
+
<input value={authRef} disabled={disabled} onChange={(e) => setAuthRef(e.target.value)} />
|
|
335
|
+
</label>
|
|
336
|
+
<label className="dm-orchestration-config__field dm-orchestration-config__field-inline">
|
|
337
|
+
<input
|
|
338
|
+
type="checkbox"
|
|
339
|
+
checked={networkAllow}
|
|
340
|
+
disabled={disabled}
|
|
341
|
+
onChange={(e) => setNetworkAllow(e.target.checked)}
|
|
342
|
+
/>
|
|
343
|
+
<span>Network allowed</span>
|
|
344
|
+
</label>
|
|
345
|
+
<label className="dm-orchestration-config__field">
|
|
346
|
+
<span>Env refs (comma-separated)</span>
|
|
347
|
+
<input value={envRefs} disabled={disabled} onChange={(e) => setEnvRefs(e.target.value)} />
|
|
348
|
+
</label>
|
|
349
|
+
<label className="dm-orchestration-config__field">
|
|
350
|
+
<span>Timeout (ms)</span>
|
|
351
|
+
<input value={timeoutMs} disabled={disabled} onChange={(e) => setTimeoutMs(e.target.value)} />
|
|
352
|
+
</label>
|
|
353
|
+
<label className="dm-orchestration-config__field">
|
|
354
|
+
<span>Instructions</span>
|
|
355
|
+
<textarea
|
|
356
|
+
rows={3}
|
|
357
|
+
value={instructions || defaultInstructions}
|
|
358
|
+
disabled={disabled}
|
|
359
|
+
onChange={(e) => setInstructions(e.target.value)}
|
|
360
|
+
/>
|
|
361
|
+
</label>
|
|
362
|
+
</div>
|
|
363
|
+
</details>
|
|
364
|
+
|
|
365
|
+
{graphError && <p className="dm-orchestration-config__error">{graphError}</p>}
|
|
366
|
+
{graphUnset && (
|
|
367
|
+
<p className="dm-orchestration-config__hint">Start a graph before creating the sandbox tool.</p>
|
|
368
|
+
)}
|
|
369
|
+
<p className="dm-orchestration-sidecar__footnote">
|
|
370
|
+
No secrets are stored. Nothing runs until you click Run sandbox after creation.
|
|
371
|
+
</p>
|
|
372
|
+
</div>
|
|
373
|
+
</div>
|
|
374
|
+
</section>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { Suspense } from "react";
|
|
3
4
|
import DataModelShell from "./components/DataModelShell.jsx";
|
|
4
5
|
|
|
5
6
|
export default function DataModelPage() {
|
|
6
|
-
return
|
|
7
|
+
return (
|
|
8
|
+
<Suspense fallback={null}>
|
|
9
|
+
<DataModelShell />
|
|
10
|
+
</Suspense>
|
|
11
|
+
);
|
|
7
12
|
}
|