@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,163 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { ArrowLeft, GitBranch } from "lucide-react";
|
|
5
|
+
import { parseSandboxRunTrace, normalizeRunRecord } from "@/lib/orchestration-run-trace";
|
|
6
|
+
import { redactSecretsFromText } from "@/lib/orchestration-graph";
|
|
7
|
+
|
|
8
|
+
export function OrchestrationRunTracePanel({
|
|
9
|
+
row,
|
|
10
|
+
objectId,
|
|
11
|
+
fieldName,
|
|
12
|
+
selectedRunId,
|
|
13
|
+
onBack,
|
|
14
|
+
onOpenGraph
|
|
15
|
+
}) {
|
|
16
|
+
const [history, setHistory] = useState([]);
|
|
17
|
+
const [historyMessage, setHistoryMessage] = useState("");
|
|
18
|
+
const [loading, setLoading] = useState(false);
|
|
19
|
+
const [activeRunId, setActiveRunId] = useState(String(selectedRunId || row?.lastRunId || "").trim());
|
|
20
|
+
|
|
21
|
+
const rowTrace = useMemo(() => parseSandboxRunTrace(row?.lastResponse), [row?.lastResponse]);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setActiveRunId(String(selectedRunId || row?.lastRunId || "").trim());
|
|
25
|
+
}, [selectedRunId, row?.lastRunId, row?.lastResponse]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const objectIdValue = String(objectId || "").trim();
|
|
29
|
+
const name = String(row?.Name || "").trim();
|
|
30
|
+
if (!objectIdValue || !name) return;
|
|
31
|
+
setLoading(true);
|
|
32
|
+
setHistoryMessage("");
|
|
33
|
+
fetch(`/api/workspace/sandbox-run?objectId=${encodeURIComponent(objectIdValue)}&name=${encodeURIComponent(name)}`, {
|
|
34
|
+
cache: "no-store"
|
|
35
|
+
})
|
|
36
|
+
.then((res) => res.json())
|
|
37
|
+
.then((payload) => {
|
|
38
|
+
if (!payload.ok) throw new Error(payload.error || "Could not load run history");
|
|
39
|
+
setHistory(Array.isArray(payload.records) ? payload.records.map(normalizeRunRecord).filter(Boolean) : []);
|
|
40
|
+
setHistoryMessage(`${payload.recordCount || 0} saved run${payload.recordCount === 1 ? "" : "s"}`);
|
|
41
|
+
})
|
|
42
|
+
.catch((err) => {
|
|
43
|
+
setHistory([]);
|
|
44
|
+
setHistoryMessage(err.message || "Could not load run history");
|
|
45
|
+
})
|
|
46
|
+
.finally(() => setLoading(false));
|
|
47
|
+
}, [objectId, row?.Name]);
|
|
48
|
+
|
|
49
|
+
const activeRecord = useMemo(() => {
|
|
50
|
+
if (activeRunId && history.length) {
|
|
51
|
+
const match = history.find((r) => r.runId === activeRunId);
|
|
52
|
+
if (match) return match;
|
|
53
|
+
}
|
|
54
|
+
if (fieldName === "lastResponse" || !activeRunId) {
|
|
55
|
+
return {
|
|
56
|
+
runId: rowTrace.runId || row?.lastRunId || "",
|
|
57
|
+
ranAt: rowTrace.ranAt || row?.lastTested || "",
|
|
58
|
+
exitCode: rowTrace.exitCode,
|
|
59
|
+
durationMs: rowTrace.durationMs,
|
|
60
|
+
error: rowTrace.error,
|
|
61
|
+
stdout: rowTrace.stdout,
|
|
62
|
+
stderr: rowTrace.stderr,
|
|
63
|
+
output: rowTrace.output
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return history[0] || null;
|
|
67
|
+
}, [activeRunId, history, rowTrace, fieldName, row?.lastRunId, row?.lastTested]);
|
|
68
|
+
|
|
69
|
+
const summary = fieldName === "lastSourceId"
|
|
70
|
+
? `Source ${String(row?.lastSourceId || "").trim()}`
|
|
71
|
+
: fieldName === "lastRunId"
|
|
72
|
+
? `Run ${String(row?.lastRunId || activeRunId || "").trim()}`
|
|
73
|
+
: "Latest sandbox run";
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<section className="dm-orchestration-trace" aria-label="Run trace viewer">
|
|
77
|
+
<header className="dm-orchestration-trace__head">
|
|
78
|
+
<button type="button" className="dm-orchestration-header__back" onClick={onBack} aria-label="Back to record">
|
|
79
|
+
<ArrowLeft size={16} />
|
|
80
|
+
</button>
|
|
81
|
+
<div>
|
|
82
|
+
<h2>Run trace</h2>
|
|
83
|
+
<p>{summary} · {row?.Name || "Sandbox tool"}</p>
|
|
84
|
+
</div>
|
|
85
|
+
{onOpenGraph && (
|
|
86
|
+
<button type="button" className="dm-btn-outline" onClick={onOpenGraph}>
|
|
87
|
+
<GitBranch size={14} aria-hidden="true" />
|
|
88
|
+
Edit graph
|
|
89
|
+
</button>
|
|
90
|
+
)}
|
|
91
|
+
</header>
|
|
92
|
+
|
|
93
|
+
<div className="dm-orchestration-trace__body">
|
|
94
|
+
<aside className="dm-orchestration-trace__list">
|
|
95
|
+
<p className="dm-orchestration-trace__list-title">Run history</p>
|
|
96
|
+
{loading && <p className="dm-orchestration-config__hint">Loading…</p>}
|
|
97
|
+
{!loading && historyMessage && <p className="dm-orchestration-config__hint">{historyMessage}</p>}
|
|
98
|
+
<button
|
|
99
|
+
type="button"
|
|
100
|
+
className={`dm-orchestration-trace__run${!activeRunId ? " is-active" : ""}`}
|
|
101
|
+
onClick={() => setActiveRunId("")}
|
|
102
|
+
>
|
|
103
|
+
<span>Row preview</span>
|
|
104
|
+
<span>{row?.status || rowTrace.status || "—"} · {row?.lastTested || rowTrace.ranAt || "—"}</span>
|
|
105
|
+
</button>
|
|
106
|
+
{history.map((record) => (
|
|
107
|
+
<button
|
|
108
|
+
key={record.runId || record.ranAt}
|
|
109
|
+
type="button"
|
|
110
|
+
className={`dm-orchestration-trace__run${activeRunId === record.runId ? " is-active" : ""}`}
|
|
111
|
+
onClick={() => setActiveRunId(record.runId)}
|
|
112
|
+
>
|
|
113
|
+
<span>{record.runId || "run"}</span>
|
|
114
|
+
<span>
|
|
115
|
+
{record.exitCode === 0 && !record.error ? "success" : "failed"}
|
|
116
|
+
{record.ranAt ? ` · ${record.ranAt}` : ""}
|
|
117
|
+
</span>
|
|
118
|
+
</button>
|
|
119
|
+
))}
|
|
120
|
+
</aside>
|
|
121
|
+
|
|
122
|
+
<div className="dm-orchestration-trace__detail">
|
|
123
|
+
<dl className="dm-orchestration-trace__meta">
|
|
124
|
+
<div><dt>Status</dt><dd>{row?.status || rowTrace.status || (activeRecord?.exitCode === 0 ? "connected" : "—")}</dd></div>
|
|
125
|
+
<div><dt>Run ID</dt><dd>{activeRecord?.runId || row?.lastRunId || "—"}</dd></div>
|
|
126
|
+
<div><dt>Exit code</dt><dd>{activeRecord?.exitCode ?? rowTrace.exitCode ?? "—"}</dd></div>
|
|
127
|
+
<div><dt>Duration</dt><dd>{activeRecord?.durationMs ?? rowTrace.durationMs ?? "—"} ms</dd></div>
|
|
128
|
+
<div><dt>Runtime</dt><dd>{rowTrace.runtime || row?.runtime || "—"}</dd></div>
|
|
129
|
+
<div><dt>Adapter</dt><dd>{rowTrace.adapter || row?.adapter || "—"}</dd></div>
|
|
130
|
+
<div><dt>Run locality</dt><dd>{rowTrace.runLocality || row?.runLocality || "—"}</dd></div>
|
|
131
|
+
<div><dt>Tested</dt><dd>{activeRecord?.ranAt || row?.lastTested || rowTrace.ranAt || "—"}</dd></div>
|
|
132
|
+
</dl>
|
|
133
|
+
|
|
134
|
+
{(activeRecord?.error || rowTrace.error) && (
|
|
135
|
+
<div className="dm-orchestration-trace__error">
|
|
136
|
+
<span>Error</span>
|
|
137
|
+
<pre>{redactSecretsFromText(activeRecord?.error || rowTrace.error)}</pre>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
<div className="dm-orchestration-trace__output">
|
|
142
|
+
<span>Stdout</span>
|
|
143
|
+
<pre>{redactSecretsFromText(activeRecord?.stdout || rowTrace.stdout || "—")}</pre>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
{(activeRecord?.output || rowTrace.output) && (
|
|
147
|
+
<div className="dm-orchestration-trace__output">
|
|
148
|
+
<span>Normalized output</span>
|
|
149
|
+
<pre>{redactSecretsFromText(activeRecord?.output || rowTrace.output)}</pre>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{(activeRecord?.stderr || rowTrace.stderr) && (
|
|
154
|
+
<div className="dm-orchestration-trace__output">
|
|
155
|
+
<span>Stderr</span>
|
|
156
|
+
<pre>{redactSecretsFromText(activeRecord?.stderr || rowTrace.stderr)}</pre>
|
|
157
|
+
</div>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
getOrchestrationGraphUiState,
|
|
11
|
+
parseOrchestrationGraph,
|
|
12
|
+
serializeOrchestrationGraph,
|
|
13
|
+
updateGraphNode,
|
|
14
|
+
validateOrchestrationGraph
|
|
15
|
+
} from "@/lib/orchestration-graph";
|
|
16
|
+
import { resolveConnectorAction } from "@/lib/orchestration-sidecar-routing";
|
|
17
|
+
import { OrchestrationGraphCanvas } from "./OrchestrationGraphCanvas.jsx";
|
|
18
|
+
import { OrchestrationGraphEmptyCanvas } from "./OrchestrationGraphEmptyCanvas.jsx";
|
|
19
|
+
import { OrchestrationNodeConfigPanel } from "./OrchestrationNodeConfigPanel.jsx";
|
|
20
|
+
|
|
21
|
+
function resolveRegistryRowForSandbox(workspaceConfig, sandboxRow) {
|
|
22
|
+
const graph = parseOrchestrationGraph(sandboxRow?.orchestrationGraph ?? sandboxRow?.orchestrationConfig);
|
|
23
|
+
const apiNode = graph?.nodes?.find((n) => n?.type === "api-registry-call");
|
|
24
|
+
const registryId = String(
|
|
25
|
+
apiNode?.config?.registryId || apiNode?.config?.integrationId || sandboxRow?.schedulerRegistryId || ""
|
|
26
|
+
).trim();
|
|
27
|
+
if (!registryId || !workspaceConfig) return null;
|
|
28
|
+
const objects = Array.isArray(workspaceConfig?.dataModel?.objects) ? workspaceConfig.dataModel.objects : [];
|
|
29
|
+
for (const objectItem of objects) {
|
|
30
|
+
if (objectItem?.objectType !== "api-registry") continue;
|
|
31
|
+
const rows = Array.isArray(objectItem.rows) ? objectItem.rows : [];
|
|
32
|
+
const match = rows.find((r) => String(r?.integrationId || "").trim() === registryId);
|
|
33
|
+
if (match) return match;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function SandboxOrchestrationEditorPanel({
|
|
39
|
+
sandboxRow,
|
|
40
|
+
workspaceConfig,
|
|
41
|
+
disabled,
|
|
42
|
+
onSaveGraph,
|
|
43
|
+
onBack
|
|
44
|
+
}) {
|
|
45
|
+
const registryRow = useMemo(
|
|
46
|
+
() => resolveRegistryRowForSandbox(workspaceConfig, sandboxRow),
|
|
47
|
+
[workspaceConfig, sandboxRow]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const [selectedNodeId, setSelectedNodeId] = useState("input");
|
|
51
|
+
const [configTab, setConfigTab] = useState("node");
|
|
52
|
+
const [graphError, setGraphError] = useState("");
|
|
53
|
+
const savedGraph = sandboxRow?.orchestrationGraph ?? sandboxRow?.orchestrationConfig;
|
|
54
|
+
const [orchestrationGraph, setOrchestrationGraph] = useState(() => parseOrchestrationGraph(savedGraph));
|
|
55
|
+
|
|
56
|
+
const graphUiState = getOrchestrationGraphUiState(
|
|
57
|
+
orchestrationGraph ?? savedGraph
|
|
58
|
+
);
|
|
59
|
+
const graphUnset = graphUiState === "unset";
|
|
60
|
+
const graphBlankShell = graphUiState === "blank-shell";
|
|
61
|
+
const nextNodeId = useMemo(
|
|
62
|
+
() => (orchestrationGraph ? getNextCanonicalNodeId(orchestrationGraph) : "input"),
|
|
63
|
+
[orchestrationGraph]
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const selectedNode = useMemo(() => {
|
|
67
|
+
if (!orchestrationGraph?.nodes || !selectedNodeId) return null;
|
|
68
|
+
return orchestrationGraph.nodes.find((n) => String(n.id) === selectedNodeId) || null;
|
|
69
|
+
}, [orchestrationGraph, selectedNodeId]);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
setOrchestrationGraph(parseOrchestrationGraph(sandboxRow?.orchestrationGraph ?? sandboxRow?.orchestrationConfig));
|
|
73
|
+
}, [sandboxRow?.orchestrationGraph, sandboxRow?.orchestrationConfig]);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (graphUnset) {
|
|
77
|
+
setGraphError("");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (graphBlankShell) {
|
|
81
|
+
setGraphError("");
|
|
82
|
+
onSaveGraph?.(serializeOrchestrationGraph(orchestrationGraph));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const validation = validateOrchestrationGraph(orchestrationGraph);
|
|
86
|
+
setGraphError(validation.ok ? "" : validation.errors[0] || "Invalid graph");
|
|
87
|
+
onSaveGraph?.(serializeOrchestrationGraph(orchestrationGraph));
|
|
88
|
+
}, [orchestrationGraph, graphUnset, graphBlankShell, onSaveGraph]);
|
|
89
|
+
|
|
90
|
+
function startFromRegistry() {
|
|
91
|
+
if (!registryRow) return;
|
|
92
|
+
setOrchestrationGraph(buildDefaultOrchestrationGraphFromRegistry(registryRow));
|
|
93
|
+
setSelectedNodeId("input");
|
|
94
|
+
setConfigTab("node");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function startBlank() {
|
|
98
|
+
setOrchestrationGraph(buildBlankOrchestrationGraphShell());
|
|
99
|
+
setSelectedNodeId("input");
|
|
100
|
+
setConfigTab("node");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function applyPastedGraph(text) {
|
|
104
|
+
const parsed = parseOrchestrationGraph(text);
|
|
105
|
+
if (parsed) setOrchestrationGraph(parsed);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function addNextNode() {
|
|
109
|
+
if (!nextNodeId) return;
|
|
110
|
+
setOrchestrationGraph((g) => addCanonicalNodeToGraph(g || buildBlankOrchestrationGraphShell(), nextNodeId, registryRow || {}));
|
|
111
|
+
setSelectedNodeId(nextNodeId);
|
|
112
|
+
setConfigTab("node");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function handleConnectorAction(payload) {
|
|
116
|
+
const { nodeId, tab } = resolveConnectorAction(payload);
|
|
117
|
+
setSelectedNodeId(nodeId);
|
|
118
|
+
setConfigTab(tab);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function handleNodeConfigChange(configPatch) {
|
|
122
|
+
if (!selectedNodeId) return;
|
|
123
|
+
setOrchestrationGraph((g) => updateGraphNode(g, selectedNodeId, configPatch));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<section className="dm-orchestration-sidecar" aria-label="Sandbox orchestration graph editor">
|
|
128
|
+
<header className="dm-orchestration-header">
|
|
129
|
+
<button type="button" className="dm-orchestration-header__back" onClick={onBack} aria-label="Back">
|
|
130
|
+
<ArrowLeft size={16} />
|
|
131
|
+
</button>
|
|
132
|
+
<div className="dm-orchestration-header__titles">
|
|
133
|
+
<h2>Orchestration graph</h2>
|
|
134
|
+
<p>{sandboxRow?.Name || "Sandbox tool"}</p>
|
|
135
|
+
</div>
|
|
136
|
+
</header>
|
|
137
|
+
|
|
138
|
+
<div className="dm-orchestration-sidecar__body">
|
|
139
|
+
<div className="dm-orchestration-sidecar__canvas-col">
|
|
140
|
+
{graphUnset ? (
|
|
141
|
+
<OrchestrationGraphEmptyCanvas
|
|
142
|
+
disabled={disabled || !registryRow}
|
|
143
|
+
onStartFromRegistry={registryRow ? startFromRegistry : undefined}
|
|
144
|
+
onStartBlank={startBlank}
|
|
145
|
+
onPasteGraph={applyPastedGraph}
|
|
146
|
+
/>
|
|
147
|
+
) : graphBlankShell ? (
|
|
148
|
+
<div className="dm-orchestration-canvas dm-orchestration-canvas--blank-shell">
|
|
149
|
+
<p className="dm-orchestration-canvas__blank-hint">Add first node</p>
|
|
150
|
+
<button type="button" className="dm-btn-outline" disabled={disabled} onClick={addNextNode}>
|
|
151
|
+
+ Add Input
|
|
152
|
+
</button>
|
|
153
|
+
</div>
|
|
154
|
+
) : (
|
|
155
|
+
<>
|
|
156
|
+
<OrchestrationGraphCanvas
|
|
157
|
+
graph={orchestrationGraph}
|
|
158
|
+
selectedNodeId={selectedNodeId}
|
|
159
|
+
onSelectNode={(node) => {
|
|
160
|
+
setSelectedNodeId(String(node?.id || ""));
|
|
161
|
+
setConfigTab("node");
|
|
162
|
+
}}
|
|
163
|
+
onConnectorAction={handleConnectorAction}
|
|
164
|
+
/>
|
|
165
|
+
{nextNodeId && (
|
|
166
|
+
<button type="button" className="dm-btn-outline dm-orchestration-canvas__add-node" disabled={disabled} onClick={addNextNode}>
|
|
167
|
+
+ Add {nextNodeId === "api-request" ? "API Registry" : nextNodeId}
|
|
168
|
+
</button>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
)}
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{graphUiState === "populated" && (
|
|
175
|
+
<div className="dm-orchestration-sidecar__config-col">
|
|
176
|
+
<OrchestrationNodeConfigPanel
|
|
177
|
+
node={selectedNode}
|
|
178
|
+
registryRow={registryRow}
|
|
179
|
+
disabled={disabled}
|
|
180
|
+
activeTab={configTab}
|
|
181
|
+
onTabChange={setConfigTab}
|
|
182
|
+
onConfigChange={handleNodeConfigChange}
|
|
183
|
+
/>
|
|
184
|
+
{graphError && <p className="dm-orchestration-config__error">{graphError}</p>}
|
|
185
|
+
</div>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
</section>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
import { summarizeOrchestrationGraph } from "@/lib/orchestration-graph";
|
|
5
|
+
|
|
6
|
+
export function SandboxToolConfirmModal({
|
|
7
|
+
open,
|
|
8
|
+
toolName,
|
|
9
|
+
authRef,
|
|
10
|
+
orchestrationGraph,
|
|
11
|
+
onConfirm,
|
|
12
|
+
onCancel,
|
|
13
|
+
creating
|
|
14
|
+
}) {
|
|
15
|
+
if (!open) return null;
|
|
16
|
+
|
|
17
|
+
const summary = summarizeOrchestrationGraph(orchestrationGraph);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="dm-orchestration-confirm dm-orchestration-confirm__backdrop" onClick={onCancel} role="presentation">
|
|
21
|
+
<section
|
|
22
|
+
className="dm-orchestration-confirm__dialog"
|
|
23
|
+
role="dialog"
|
|
24
|
+
aria-modal="true"
|
|
25
|
+
aria-labelledby="sandbox-tool-confirm-title"
|
|
26
|
+
onClick={(event) => event.stopPropagation()}
|
|
27
|
+
>
|
|
28
|
+
<header className="dm-orchestration-confirm__head">
|
|
29
|
+
<div>
|
|
30
|
+
<p>Confirm</p>
|
|
31
|
+
<h2 id="sandbox-tool-confirm-title">Create sandbox tool?</h2>
|
|
32
|
+
</div>
|
|
33
|
+
<button type="button" className="dm-sidebar-close" onClick={onCancel} aria-label="Close">
|
|
34
|
+
<X size={16} />
|
|
35
|
+
</button>
|
|
36
|
+
</header>
|
|
37
|
+
<div className="dm-orchestration-confirm__body">
|
|
38
|
+
<p>This creates one Sandbox Environment row from the tested API Registry record.</p>
|
|
39
|
+
<ul className="dm-orchestration-confirm__list">
|
|
40
|
+
<li>Saves orchestrationGraph on the sandbox row</li>
|
|
41
|
+
<li>Stores <code>{authRef || "authRef"}</code> only — no secrets</li>
|
|
42
|
+
<li>Does not store secrets</li>
|
|
43
|
+
<li>Does not create widgets</li>
|
|
44
|
+
<li>Does not change dashboards</li>
|
|
45
|
+
<li>Does not change canvas</li>
|
|
46
|
+
<li>Does not run until you click Run sandbox</li>
|
|
47
|
+
</ul>
|
|
48
|
+
<p className="dm-orchestration-confirm__summary">
|
|
49
|
+
<span>Run plan</span>
|
|
50
|
+
{summary}
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
<footer className="dm-orchestration-confirm__foot">
|
|
54
|
+
<button type="button" className="dm-btn-outline" disabled={creating} onClick={onCancel}>
|
|
55
|
+
Cancel
|
|
56
|
+
</button>
|
|
57
|
+
<button type="button" className="dm-btn-primary-sm" disabled={creating} onClick={onConfirm}>
|
|
58
|
+
{creating ? "Creating…" : "Create tool"}
|
|
59
|
+
</button>
|
|
60
|
+
</footer>
|
|
61
|
+
</section>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|