@growthub/cli 0.13.2 → 0.13.5
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/metadata-graph/route.js +184 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +24 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +14 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +74 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +67 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +77 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +72 -4
- 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 +123 -27
- 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 +224 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +754 -92
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +224 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +32 -1
- 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 +530 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +8 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +10 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/RunSetupPanel.jsx +261 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +119 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +779 -138
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +91 -14
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +35 -0
- 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 +28 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +216 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +412 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +366 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +34 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-eligibility.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-redaction.js +64 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +665 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-host-catalog.js +168 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +595 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +164 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +11 -0
- 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 +111 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { Play, X } from "lucide-react";
|
|
5
|
+
import { RUN_INPUTS_KIND } from "@/lib/orchestration-run-inputs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* RunSetupPanel — sidecar surface for collecting manual run inputs before a
|
|
9
|
+
* workflow execution. Mounted by WorkflowSurface only when the active
|
|
10
|
+
* orchestration graph declares a `human-input` / form node with required
|
|
11
|
+
* fields. For workflows with no manual inputs, this panel never renders.
|
|
12
|
+
*
|
|
13
|
+
* Invariants:
|
|
14
|
+
* - UI-only validation. The server validates again before persisting.
|
|
15
|
+
* - No secret values are accepted. Fields typed `secretRef` collect only
|
|
16
|
+
* the ref slug. Secret-looking keys (api_key, token, password, etc.)
|
|
17
|
+
* are coerced to secretRef on the server.
|
|
18
|
+
* - The panel reuses .dm-workflow-panel-head, .dm-orchestration-config*,
|
|
19
|
+
* .dm-btn-outline, .dm-workflow-chip-btn. No new design system.
|
|
20
|
+
*/
|
|
21
|
+
export function RunSetupPanel({ schema, running, onSubmit, onCancel }) {
|
|
22
|
+
const fields = Array.isArray(schema?.fields) ? schema.fields : [];
|
|
23
|
+
const [values, setValues] = useState(() => seedValues(fields));
|
|
24
|
+
const [touched, setTouched] = useState({});
|
|
25
|
+
|
|
26
|
+
const missing = useMemo(
|
|
27
|
+
() => fields.filter((f) => f.required && !hasValue(values[f.id], f)).map((f) => f.id),
|
|
28
|
+
[fields, values]
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
function patch(fieldId, raw) {
|
|
32
|
+
setValues((current) => ({ ...current, [fieldId]: raw }));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function markTouched(fieldId) {
|
|
36
|
+
setTouched((current) => ({ ...current, [fieldId]: true }));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function submit() {
|
|
40
|
+
if (missing.length > 0) {
|
|
41
|
+
const next = {};
|
|
42
|
+
for (const id of missing) next[id] = true;
|
|
43
|
+
setTouched((current) => ({ ...current, ...next }));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const envelope = {
|
|
47
|
+
kind: RUN_INPUTS_KIND,
|
|
48
|
+
source: "manual",
|
|
49
|
+
values: Object.fromEntries(
|
|
50
|
+
fields
|
|
51
|
+
.map((field) => [field.id, normalizeForSubmission(field, values[field.id])])
|
|
52
|
+
.filter(([, value]) => value !== undefined)
|
|
53
|
+
),
|
|
54
|
+
files: []
|
|
55
|
+
};
|
|
56
|
+
onSubmit?.(envelope);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!schema?.requiresInput || fields.length === 0) {
|
|
60
|
+
return (
|
|
61
|
+
<div className="dm-run-setup">
|
|
62
|
+
<p className="dm-run-setup__hint">This workflow has no manual inputs configured.</p>
|
|
63
|
+
<div className="dm-run-setup__actions">
|
|
64
|
+
<button type="button" className="dm-btn-outline" onClick={onCancel}>Close</button>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className="dm-run-setup">
|
|
72
|
+
{schema.instructions ? (
|
|
73
|
+
<p className="dm-run-setup__instructions">{schema.instructions}</p>
|
|
74
|
+
) : null}
|
|
75
|
+
|
|
76
|
+
<div className="dm-run-setup__fields">
|
|
77
|
+
{fields.map((field) => (
|
|
78
|
+
<RunSetupField
|
|
79
|
+
key={field.id}
|
|
80
|
+
field={field}
|
|
81
|
+
value={values[field.id]}
|
|
82
|
+
touched={Boolean(touched[field.id])}
|
|
83
|
+
onChange={(raw) => patch(field.id, raw)}
|
|
84
|
+
onBlur={() => markTouched(field.id)}
|
|
85
|
+
/>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<div className="dm-run-setup__notice">
|
|
90
|
+
Inputs are validated server-side before the run. Secret-typed fields collect a ref slug only — never a raw value.
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div className="dm-run-setup__actions">
|
|
94
|
+
<button type="button" className="dm-btn-outline" onClick={onCancel} disabled={running}>
|
|
95
|
+
<X size={13} aria-hidden="true" /> Cancel
|
|
96
|
+
</button>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
className="dm-workflow-chip-btn"
|
|
100
|
+
onClick={submit}
|
|
101
|
+
disabled={running || missing.length > 0}
|
|
102
|
+
title={missing.length > 0 ? `Fill required fields: ${missing.join(", ")}` : "Run workflow with these inputs"}
|
|
103
|
+
>
|
|
104
|
+
<Play size={13} aria-hidden="true" /> {running ? "Running" : "Run workflow"}
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function RunSetupField({ field, value, touched, onChange, onBlur }) {
|
|
112
|
+
const showError = touched && field.required && !hasValue(value, field);
|
|
113
|
+
const inputId = `dm-run-setup-${field.id}`;
|
|
114
|
+
const help = field.helpText || (field.type === "secretRef" ? "Reference slug only — server resolves the secret." : "");
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<label className="dm-orchestration-config__field" htmlFor={inputId}>
|
|
118
|
+
<span>
|
|
119
|
+
{field.label}
|
|
120
|
+
{field.required ? <em aria-hidden="true" className="dm-run-setup__required"> *</em> : null}
|
|
121
|
+
</span>
|
|
122
|
+
{renderInput(field, value, onChange, onBlur, inputId)}
|
|
123
|
+
{help ? <small className="dm-run-setup__help">{help}</small> : null}
|
|
124
|
+
{showError ? <small className="dm-run-setup__error">Required.</small> : null}
|
|
125
|
+
</label>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function renderInput(field, value, onChange, onBlur, inputId) {
|
|
130
|
+
const safeValue = value == null ? "" : value;
|
|
131
|
+
switch (field.type) {
|
|
132
|
+
case "textarea":
|
|
133
|
+
case "json":
|
|
134
|
+
return (
|
|
135
|
+
<textarea
|
|
136
|
+
id={inputId}
|
|
137
|
+
rows={field.type === "json" ? 6 : 4}
|
|
138
|
+
value={typeof safeValue === "string" ? safeValue : JSON.stringify(safeValue, null, 2)}
|
|
139
|
+
onChange={(e) => onChange(e.target.value)}
|
|
140
|
+
onBlur={onBlur}
|
|
141
|
+
placeholder={field.type === "json" ? "{ }" : ""}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
case "boolean":
|
|
145
|
+
case "checkbox":
|
|
146
|
+
return (
|
|
147
|
+
<span className="dm-run-setup__checkbox">
|
|
148
|
+
<input
|
|
149
|
+
id={inputId}
|
|
150
|
+
type="checkbox"
|
|
151
|
+
checked={Boolean(safeValue)}
|
|
152
|
+
onChange={(e) => onChange(e.target.checked)}
|
|
153
|
+
onBlur={onBlur}
|
|
154
|
+
/>
|
|
155
|
+
<span>{field.helpText || "Enable"}</span>
|
|
156
|
+
</span>
|
|
157
|
+
);
|
|
158
|
+
case "number":
|
|
159
|
+
case "integer":
|
|
160
|
+
return (
|
|
161
|
+
<input
|
|
162
|
+
id={inputId}
|
|
163
|
+
type="number"
|
|
164
|
+
value={safeValue === "" ? "" : safeValue}
|
|
165
|
+
onChange={(e) => onChange(e.target.value)}
|
|
166
|
+
onBlur={onBlur}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
case "email":
|
|
170
|
+
return (
|
|
171
|
+
<input
|
|
172
|
+
id={inputId}
|
|
173
|
+
type="email"
|
|
174
|
+
value={safeValue}
|
|
175
|
+
onChange={(e) => onChange(e.target.value)}
|
|
176
|
+
onBlur={onBlur}
|
|
177
|
+
placeholder="name@example.com"
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
case "url":
|
|
181
|
+
return (
|
|
182
|
+
<input
|
|
183
|
+
id={inputId}
|
|
184
|
+
type="url"
|
|
185
|
+
value={safeValue}
|
|
186
|
+
onChange={(e) => onChange(e.target.value)}
|
|
187
|
+
onBlur={onBlur}
|
|
188
|
+
placeholder="https://"
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
case "secretRef":
|
|
192
|
+
return (
|
|
193
|
+
<input
|
|
194
|
+
id={inputId}
|
|
195
|
+
type="text"
|
|
196
|
+
value={safeValue}
|
|
197
|
+
onChange={(e) => onChange(e.target.value)}
|
|
198
|
+
onBlur={onBlur}
|
|
199
|
+
placeholder="ENV_REF_SLUG"
|
|
200
|
+
autoComplete="off"
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
default:
|
|
204
|
+
return (
|
|
205
|
+
<input
|
|
206
|
+
id={inputId}
|
|
207
|
+
type="text"
|
|
208
|
+
value={safeValue}
|
|
209
|
+
onChange={(e) => onChange(e.target.value)}
|
|
210
|
+
onBlur={onBlur}
|
|
211
|
+
/>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function seedValues(fields) {
|
|
217
|
+
const out = {};
|
|
218
|
+
for (const field of fields) {
|
|
219
|
+
if (field.type === "boolean" || field.type === "checkbox") out[field.id] = false;
|
|
220
|
+
else out[field.id] = "";
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function hasValue(value, field) {
|
|
226
|
+
if (field?.type === "boolean" || field?.type === "checkbox") return value === true;
|
|
227
|
+
if (value == null) return false;
|
|
228
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
229
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
230
|
+
return Boolean(value);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function normalizeForSubmission(field, value) {
|
|
234
|
+
if (field.type === "boolean" || field.type === "checkbox") return Boolean(value);
|
|
235
|
+
if (field.type === "secretRef") {
|
|
236
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
237
|
+
return text ? { secretRef: text } : undefined;
|
|
238
|
+
}
|
|
239
|
+
if (field.type === "number" || field.type === "integer") {
|
|
240
|
+
if (value === "" || value == null) return undefined;
|
|
241
|
+
const num = Number(value);
|
|
242
|
+
return Number.isFinite(num) ? num : undefined;
|
|
243
|
+
}
|
|
244
|
+
if (field.type === "json") {
|
|
245
|
+
if (typeof value === "string") {
|
|
246
|
+
const trimmed = value.trim();
|
|
247
|
+
if (!trimmed) return undefined;
|
|
248
|
+
try {
|
|
249
|
+
return JSON.parse(trimmed);
|
|
250
|
+
} catch {
|
|
251
|
+
return trimmed;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
if (typeof value === "string") {
|
|
257
|
+
const text = value.trim();
|
|
258
|
+
return text ? text : undefined;
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
@@ -31,9 +31,11 @@ import { findSandboxRowByWorkflowRef } from "@/lib/nav-workflows";
|
|
|
31
31
|
import {
|
|
32
32
|
addCanonicalNodeToGraph,
|
|
33
33
|
buildBlankOrchestrationGraphShell,
|
|
34
|
+
buildDefaultAgentSwarmGraph,
|
|
34
35
|
buildDefaultOrchestrationGraphFromRegistry,
|
|
35
36
|
getNextCanonicalNodeId,
|
|
36
37
|
getOrchestrationGraphUiState,
|
|
38
|
+
isAgentSwarmGraph,
|
|
37
39
|
parseOrchestrationGraph,
|
|
38
40
|
redactSecretsFromText,
|
|
39
41
|
serializeOrchestrationGraph,
|
|
@@ -45,6 +47,19 @@ import { OrchestrationGraphCanvas } from "../data-model/components/Orchestration
|
|
|
45
47
|
import { OrchestrationGraphEmptyCanvas } from "../data-model/components/OrchestrationGraphEmptyCanvas.jsx";
|
|
46
48
|
import { OrchestrationNodeConfigPanel } from "../data-model/components/OrchestrationNodeConfigPanel.jsx";
|
|
47
49
|
import { OrchestrationRunTracePanel } from "../data-model/components/OrchestrationRunTracePanel.jsx";
|
|
50
|
+
import { AgentSwarmPanel } from "../data-model/components/AgentSwarmPanel.jsx";
|
|
51
|
+
import { RunSetupPanel } from "./RunSetupPanel.jsx";
|
|
52
|
+
import { describeRunInputMetadataItems, discoverRunInputSchema } from "@/lib/orchestration-run-inputs";
|
|
53
|
+
import { selectWorkflowNodeInputSchema } from "@/lib/workspace-metadata-selectors";
|
|
54
|
+
|
|
55
|
+
// Workspace Metadata Graph V1 — read-only dependency metadata for workflow
|
|
56
|
+
// sidecars. The runtime path (sandbox-run, publish, draft/live) is
|
|
57
|
+
// unchanged; this only exposes typed dependency descriptors so the sidecar
|
|
58
|
+
// can render "this node requires N inputs from M source nodes".
|
|
59
|
+
const WORKFLOW_METADATA_SELECTORS = Object.freeze({
|
|
60
|
+
describeRunInputMetadataItems,
|
|
61
|
+
selectWorkflowNodeInputSchema
|
|
62
|
+
});
|
|
48
63
|
|
|
49
64
|
function resolveRegistryRowForSandbox(workspaceConfig, sandboxRow) {
|
|
50
65
|
const graph = parseOrchestrationGraph(sandboxRow?.orchestrationGraph);
|
|
@@ -328,6 +343,7 @@ export default function WorkflowSurface() {
|
|
|
328
343
|
const [graphError, setGraphError] = useState("");
|
|
329
344
|
const [orchestrationGraph, setOrchestrationGraph] = useState(null);
|
|
330
345
|
const [dirty, setDirty] = useState(false);
|
|
346
|
+
const [runSetupOpen, setRunSetupOpen] = useState(false);
|
|
331
347
|
|
|
332
348
|
const load = useCallback(async () => {
|
|
333
349
|
setLoading(true);
|
|
@@ -379,9 +395,10 @@ export default function WorkflowSurface() {
|
|
|
379
395
|
const graphUiState = getOrchestrationGraphUiState(orchestrationGraph);
|
|
380
396
|
const graphUnset = graphUiState === "unset";
|
|
381
397
|
const graphBlankShell = graphUiState === "blank-shell";
|
|
398
|
+
const swarmMode = useMemo(() => isAgentSwarmGraph(orchestrationGraph), [orchestrationGraph]);
|
|
382
399
|
const nextNodeId = useMemo(
|
|
383
|
-
() => (orchestrationGraph ? getNextCanonicalNodeId(orchestrationGraph) :
|
|
384
|
-
[orchestrationGraph]
|
|
400
|
+
() => (orchestrationGraph && !swarmMode ? getNextCanonicalNodeId(orchestrationGraph) : null),
|
|
401
|
+
[orchestrationGraph, swarmMode]
|
|
385
402
|
);
|
|
386
403
|
|
|
387
404
|
const selectedNode = useMemo(() => {
|
|
@@ -446,6 +463,21 @@ export default function WorkflowSurface() {
|
|
|
446
463
|
}
|
|
447
464
|
}
|
|
448
465
|
|
|
466
|
+
async function patchSandboxRuntimeFields(fields) {
|
|
467
|
+
if (resolved.rowIndex < 0 || !objectId || !fields || typeof fields !== "object") return;
|
|
468
|
+
setSaving(true);
|
|
469
|
+
setSaveMessage("");
|
|
470
|
+
try {
|
|
471
|
+
const next = patchSandboxRowInConfig(workspaceConfig, objectId, resolved.rowIndex, fields);
|
|
472
|
+
await persistWorkspace(next);
|
|
473
|
+
setSaveMessage("Updated workflow runtime settings.");
|
|
474
|
+
} catch (err) {
|
|
475
|
+
setSaveMessage(err.message || "Runtime update failed");
|
|
476
|
+
} finally {
|
|
477
|
+
setSaving(false);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
449
481
|
async function publishGraph() {
|
|
450
482
|
if (resolved.rowIndex < 0 || !objectId) return;
|
|
451
483
|
const serialized = serializeCurrentGraph();
|
|
@@ -536,17 +568,27 @@ export default function WorkflowSurface() {
|
|
|
536
568
|
}
|
|
537
569
|
}
|
|
538
570
|
|
|
539
|
-
|
|
571
|
+
const runInputSchema = useMemo(
|
|
572
|
+
() => discoverRunInputSchema(orchestrationGraph),
|
|
573
|
+
[orchestrationGraph]
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
async function runSandbox(options = {}) {
|
|
540
577
|
if (!objectId || !rowId) return;
|
|
578
|
+
const runInputs = options && typeof options === "object" && options.runInputs && typeof options.runInputs === "object"
|
|
579
|
+
? options.runInputs
|
|
580
|
+
: null;
|
|
541
581
|
setRunning(true);
|
|
542
582
|
setRunMessage("");
|
|
543
583
|
try {
|
|
544
584
|
const draft = await saveDraft({ orchestrationDraftStatus: "testing" });
|
|
545
585
|
const draftGraph = draft?.serialized || serializeCurrentGraph();
|
|
586
|
+
const body = { objectId, name: rowId, useDraft: true, draftGraph };
|
|
587
|
+
if (runInputs) body.runInputs = runInputs;
|
|
546
588
|
const res = await fetch("/api/workspace/sandbox-run", {
|
|
547
589
|
method: "POST",
|
|
548
590
|
headers: { "content-type": "application/json" },
|
|
549
|
-
body: JSON.stringify(
|
|
591
|
+
body: JSON.stringify(body),
|
|
550
592
|
});
|
|
551
593
|
const payload = await res.json();
|
|
552
594
|
const responseText = redactSecretsFromText(JSON.stringify(payload.response ?? payload, null, 2));
|
|
@@ -586,6 +628,21 @@ export default function WorkflowSurface() {
|
|
|
586
628
|
setSidecarMode("trace");
|
|
587
629
|
}
|
|
588
630
|
|
|
631
|
+
function handleTestClick() {
|
|
632
|
+
if (runInputSchema.requiresInput) {
|
|
633
|
+
setRunSetupOpen(true);
|
|
634
|
+
setSelectedNodeId("");
|
|
635
|
+
setAddTarget(null);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
runSandbox();
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function handleRunWithInputs(runInputs) {
|
|
642
|
+
setRunSetupOpen(false);
|
|
643
|
+
await runSandbox({ runInputs });
|
|
644
|
+
}
|
|
645
|
+
|
|
589
646
|
function openGraphMode() {
|
|
590
647
|
const params = new URLSearchParams(searchParams.toString());
|
|
591
648
|
params.delete("run");
|
|
@@ -606,6 +663,16 @@ export default function WorkflowSurface() {
|
|
|
606
663
|
setDirty(true);
|
|
607
664
|
}
|
|
608
665
|
|
|
666
|
+
function startAgentSwarm() {
|
|
667
|
+
const graph = buildDefaultAgentSwarmGraph({
|
|
668
|
+
agentHost: String(sandboxRow?.agentHost || "").trim()
|
|
669
|
+
});
|
|
670
|
+
setOrchestrationGraph(graph);
|
|
671
|
+
setSelectedNodeId("orchestrator");
|
|
672
|
+
setConfigTab("swarm");
|
|
673
|
+
setDirty(true);
|
|
674
|
+
}
|
|
675
|
+
|
|
609
676
|
function applyPastedGraph(text) {
|
|
610
677
|
const parsed = parseOrchestrationGraph(text);
|
|
611
678
|
if (parsed) {
|
|
@@ -759,8 +826,8 @@ export default function WorkflowSurface() {
|
|
|
759
826
|
</button>
|
|
760
827
|
)}
|
|
761
828
|
{canTest && (
|
|
762
|
-
<button type="button" className="dm-workflow-chip-btn" disabled={running || saving || publishing} onClick={
|
|
763
|
-
<Play size={13} /> {running ? "Running" : "Test"}
|
|
829
|
+
<button type="button" className="dm-workflow-chip-btn" disabled={running || saving || publishing} onClick={handleTestClick}>
|
|
830
|
+
<Play size={13} /> {running ? "Running" : runInputSchema.requiresInput ? "Test with inputs" : "Test"}
|
|
764
831
|
</button>
|
|
765
832
|
)}
|
|
766
833
|
{showPublish && (
|
|
@@ -816,9 +883,11 @@ export default function WorkflowSurface() {
|
|
|
816
883
|
selectedRunId={runId}
|
|
817
884
|
onBack={openGraphMode}
|
|
818
885
|
onOpenGraph={openGraphMode}
|
|
886
|
+
onReplay={runSandbox}
|
|
887
|
+
running={running}
|
|
819
888
|
/>
|
|
820
889
|
) : (
|
|
821
|
-
<div className={`dm-orchestration-sidecar dm-workflow-orchestration${selectedNode || addTarget ? " has-panel" : ""}`}>
|
|
890
|
+
<div className={`dm-orchestration-sidecar dm-workflow-orchestration${selectedNode || addTarget || runSetupOpen ? " has-panel" : ""}`}>
|
|
822
891
|
<div className="dm-orchestration-sidecar__body">
|
|
823
892
|
<div className="dm-orchestration-sidecar__canvas-col">
|
|
824
893
|
{graphUnset ? (
|
|
@@ -826,6 +895,7 @@ export default function WorkflowSurface() {
|
|
|
826
895
|
disabled={false}
|
|
827
896
|
onStartFromRegistry={registryRow ? startFromRegistry : undefined}
|
|
828
897
|
onStartBlank={startBlank}
|
|
898
|
+
onStartAgentSwarm={startAgentSwarm}
|
|
829
899
|
onPasteGraph={applyPastedGraph}
|
|
830
900
|
/>
|
|
831
901
|
) : graphBlankShell ? (
|
|
@@ -855,7 +925,24 @@ export default function WorkflowSurface() {
|
|
|
855
925
|
</>
|
|
856
926
|
)}
|
|
857
927
|
</div>
|
|
858
|
-
{graphUiState === "populated" &&
|
|
928
|
+
{graphUiState === "populated" && runSetupOpen && (
|
|
929
|
+
<div className="dm-orchestration-sidecar__config-col">
|
|
930
|
+
<div className="dm-workflow-panel-head">
|
|
931
|
+
<button type="button" className="dm-workflow-icon-btn" onClick={() => setRunSetupOpen(false)} aria-label="Close run setup panel">
|
|
932
|
+
<X size={14} />
|
|
933
|
+
</button>
|
|
934
|
+
<span>Run setup</span>
|
|
935
|
+
<em>Manual inputs</em>
|
|
936
|
+
</div>
|
|
937
|
+
<RunSetupPanel
|
|
938
|
+
schema={runInputSchema}
|
|
939
|
+
running={running}
|
|
940
|
+
onSubmit={handleRunWithInputs}
|
|
941
|
+
onCancel={() => setRunSetupOpen(false)}
|
|
942
|
+
/>
|
|
943
|
+
</div>
|
|
944
|
+
)}
|
|
945
|
+
{graphUiState === "populated" && !runSetupOpen && addTarget && (
|
|
859
946
|
<div className="dm-orchestration-sidecar__config-col">
|
|
860
947
|
<div className="dm-workflow-panel-head">
|
|
861
948
|
<button type="button" className="dm-workflow-icon-btn" onClick={() => setAddTarget(null)} aria-label="Close side panel">
|
|
@@ -870,7 +957,27 @@ export default function WorkflowSurface() {
|
|
|
870
957
|
/>
|
|
871
958
|
</div>
|
|
872
959
|
)}
|
|
873
|
-
{graphUiState === "populated" && !addTarget && selectedNode && (
|
|
960
|
+
{graphUiState === "populated" && !runSetupOpen && !addTarget && selectedNode && swarmMode && selectedNode?.type === "thinAdapter" && (
|
|
961
|
+
<div className="dm-orchestration-sidecar__config-col">
|
|
962
|
+
<div className="dm-workflow-panel-head">
|
|
963
|
+
<button type="button" className="dm-workflow-icon-btn" onClick={() => setSelectedNodeId("")} aria-label="Close side panel">
|
|
964
|
+
<X size={14} />
|
|
965
|
+
</button>
|
|
966
|
+
<span>Agent swarm</span>
|
|
967
|
+
<em>agent-swarm-v1</em>
|
|
968
|
+
</div>
|
|
969
|
+
<AgentSwarmPanel
|
|
970
|
+
graph={orchestrationGraph}
|
|
971
|
+
disabled={false}
|
|
972
|
+
onGraphChange={(updater) => {
|
|
973
|
+
setOrchestrationGraph((g) => (typeof updater === "function" ? updater(g) : updater));
|
|
974
|
+
setDirty(true);
|
|
975
|
+
}}
|
|
976
|
+
/>
|
|
977
|
+
{graphError && <p className="dm-orchestration-config__error">{graphError}</p>}
|
|
978
|
+
</div>
|
|
979
|
+
)}
|
|
980
|
+
{graphUiState === "populated" && !runSetupOpen && !addTarget && selectedNode && !(swarmMode && selectedNode?.type === "thinAdapter") && (
|
|
874
981
|
<div className="dm-orchestration-sidecar__config-col">
|
|
875
982
|
<div className="dm-workflow-panel-head">
|
|
876
983
|
<button type="button" className="dm-workflow-icon-btn" onClick={() => setSelectedNodeId("")} aria-label="Close side panel">
|
|
@@ -884,6 +991,9 @@ export default function WorkflowSurface() {
|
|
|
884
991
|
registryRow={registryRow}
|
|
885
992
|
workspaceConfig={workspaceConfig}
|
|
886
993
|
sandboxRow={sandboxRow}
|
|
994
|
+
objectId={objectId}
|
|
995
|
+
rowName={rowId}
|
|
996
|
+
onSandboxRowPatch={patchSandboxRuntimeFields}
|
|
887
997
|
onDeleteNode={deleteSelectedNode}
|
|
888
998
|
disabled={false}
|
|
889
999
|
activeTab={configTab}
|