@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.
Files changed (27) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +50 -25
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryActionCard.jsx +141 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryReviewModal.jsx +38 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +522 -35
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +242 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +52 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +1203 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +163 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxOrchestrationEditorPanel.jsx +190 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolConfirmModal.jsx +64 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxToolDraftPanel.jsx +376 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +6 -1
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +1062 -2
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +10 -7
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +906 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/page.jsx +12 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +492 -28
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +114 -30
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +1 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/nav-workflows.js +54 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +322 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +734 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +73 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-sidecar-routing.js +24 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +21 -1
  27. package/package.json +1 -1
@@ -0,0 +1,242 @@
1
+ "use client";
2
+
3
+ import { useMemo, useState } from "react";
4
+ import {
5
+ ArrowDownToLine,
6
+ Bot,
7
+ Filter,
8
+ Globe,
9
+ Maximize2,
10
+ Minus,
11
+ Plus,
12
+ Settings,
13
+ SlidersHorizontal,
14
+ Target,
15
+ ZoomIn,
16
+ ZoomOut
17
+ } from "lucide-react";
18
+ import { orderedGraphNodes, parseOrchestrationGraph } from "@/lib/orchestration-graph";
19
+
20
+ const NODE_TYPE_LABELS = {
21
+ input: "Input",
22
+ "api-registry-call": "API Registry",
23
+ "transform-filter": "Transform",
24
+ "normalize-output": "Transform",
25
+ "tool-result": "Result",
26
+ thinAdapter: "Agent",
27
+ "data-trigger": "Trigger",
28
+ "data-action": "Action",
29
+ "ai-agent": "AI",
30
+ "flow-control": "Flow",
31
+ "core-action": "Core",
32
+ "human-input": "Input"
33
+ };
34
+
35
+ const NODE_ICONS = {
36
+ input: SlidersHorizontal,
37
+ "api-registry-call": Globe,
38
+ "transform-filter": Filter,
39
+ "normalize-output": Filter,
40
+ "tool-result": Target,
41
+ thinAdapter: Bot,
42
+ "data-trigger": SlidersHorizontal,
43
+ "data-action": Plus,
44
+ "ai-agent": Bot,
45
+ "flow-control": Settings,
46
+ "core-action": Globe,
47
+ "human-input": SlidersHorizontal
48
+ };
49
+
50
+ const CONNECTOR_OPTIONS = [
51
+ { id: "filter", label: "Add filter" },
52
+ { id: "map", label: "Map fields" },
53
+ { id: "preview", label: "Preview output" }
54
+ ];
55
+
56
+ function nodeSubtitle(node) {
57
+ const config = node?.config || {};
58
+ if (node?.subtitle) return String(node.subtitle);
59
+ if (node?.type === "api-registry-call") {
60
+ const id = String(config.integrationId || config.registryId || "").trim();
61
+ const method = String(config.method || "GET").trim().toUpperCase();
62
+ const endpoint = String(config.endpoint || "").trim();
63
+ return endpoint ? `${id} · ${method} ${endpoint}` : id;
64
+ }
65
+ if (node?.type === "transform-filter" || node?.type === "normalize-output") {
66
+ return "Map fields and filter rows";
67
+ }
68
+ if (node?.type === "input") return "Manual or source payload";
69
+ if (node?.type === "tool-result") return "Save status and response";
70
+ if (node?.type === "thinAdapter") return node?.sandbox ? String(node.sandbox) : "Local agent step";
71
+ return "";
72
+ }
73
+
74
+ function hoverHint(node) {
75
+ const type = String(node?.type || "");
76
+ if (type === "input") return "Configure input";
77
+ if (type === "api-registry-call") return "Configure API request";
78
+ if (type === "transform-filter" || type === "normalize-output") return "Map response fields";
79
+ if (type === "tool-result") return "Result settings";
80
+ if (type === "thinAdapter") return "Configure agent step";
81
+ return "Configure node";
82
+ }
83
+
84
+ function normalizedNodeType(node) {
85
+ const type = String(node?.type || "").trim();
86
+ if (type === "thinAdapter") return "AI Model";
87
+ return type || "node";
88
+ }
89
+
90
+ function nodeRecordName(node) {
91
+ if (node?.type === "thinAdapter") return String(node?.sandbox || node?.id || "").trim();
92
+ if (node?.config?.objectName) return String(node.config.objectName).trim();
93
+ if (node?.config?.objectId) return String(node.config.objectId).trim();
94
+ return "";
95
+ }
96
+
97
+ export function OrchestrationGraphCanvas({
98
+ graph,
99
+ selectedNodeId,
100
+ onSelectNode,
101
+ onConnectorAction,
102
+ showRunTest,
103
+ onRunTest,
104
+ runStatus,
105
+ runMessage,
106
+ statusLabel = "Draft",
107
+ }) {
108
+ const parsed = useMemo(() => parseOrchestrationGraph(graph) || graph, [graph]);
109
+ const nodes = useMemo(() => orderedGraphNodes(parsed), [parsed]);
110
+ const edges = useMemo(() => (Array.isArray(parsed?.edges) ? parsed.edges : []), [parsed]);
111
+ const [internalSelected, setInternalSelected] = useState(null);
112
+ const [connectorPopover, setConnectorPopover] = useState(null);
113
+ const [zoom, setZoom] = useState(1);
114
+ const activeId = selectedNodeId ?? internalSelected;
115
+
116
+ if (!nodes.length) {
117
+ return (
118
+ <div className="dm-orchestration-canvas dm-orchestration-canvas--empty">
119
+ <p>No orchestration nodes configured.</p>
120
+ </div>
121
+ );
122
+ }
123
+
124
+ function edgeBetween(fromId, toId) {
125
+ return edges.find((e) => String(e.from) === fromId && String(e.to) === toId);
126
+ }
127
+
128
+ return (
129
+ <div className="dm-orchestration-canvas" aria-label="Orchestration graph field editor">
130
+ <span className={`dm-orchestration-canvas__badge is-${String(statusLabel || "draft").toLowerCase()}`}>{statusLabel}</span>
131
+ <div className="dm-orchestration-floating-tools" aria-label="Canvas tools">
132
+ <button type="button" title="Add node" aria-label="Add node" onClick={() => onConnectorAction?.({ action: "add-step", from: String(nodes[nodes.length - 1]?.id || ""), to: "" })}>
133
+ <Plus size={14} />
134
+ </button>
135
+ <button type="button" title="Tidy workflow" aria-label="Tidy workflow">
136
+ <Settings size={14} />
137
+ </button>
138
+ <button type="button" title="Zoom in" aria-label="Zoom in" onClick={() => setZoom((value) => Math.min(1.4, Number((value + 0.1).toFixed(2))))}>
139
+ <ZoomIn size={14} />
140
+ </button>
141
+ <button type="button" title="Zoom out" aria-label="Zoom out" onClick={() => setZoom((value) => Math.max(0.7, Number((value - 0.1).toFixed(2))))}>
142
+ <ZoomOut size={14} />
143
+ </button>
144
+ <button type="button" title="Reset zoom" aria-label="Reset zoom" onClick={() => setZoom(1)}>
145
+ <Maximize2 size={14} />
146
+ </button>
147
+ </div>
148
+ <div className="dm-orchestration-canvas__viewport" style={{ transform: `scale(${zoom})` }}>
149
+ {nodes.map((node, index) => {
150
+ const id = String(node.id || "");
151
+ const isSelected = activeId === id;
152
+ const prevId = index > 0 ? String(nodes[index - 1].id || "") : "";
153
+ const Icon = NODE_ICONS[node.type] || ArrowDownToLine;
154
+
155
+ return (
156
+ <div key={id || index} className="dm-orchestration-canvas__step">
157
+ {index > 0 && (
158
+ <div className="dm-orchestration-connector">
159
+ <div className="dm-orchestration-connector__line" aria-hidden="true" />
160
+ <button
161
+ type="button"
162
+ className="dm-orchestration-connector__add"
163
+ aria-label="Add step"
164
+ onClick={() => {
165
+ onConnectorAction?.({ action: "add-step", from: prevId, to: id });
166
+ setConnectorPopover(null);
167
+ }}
168
+ >
169
+ <Plus size={14} />
170
+ </button>
171
+ {connectorPopover === `${prevId}-${id}` && (
172
+ <div className="dm-orchestration-connector__popover" role="menu">
173
+ <p>Add a step between these nodes</p>
174
+ {CONNECTOR_OPTIONS.map((opt) => (
175
+ <button
176
+ key={opt.id}
177
+ type="button"
178
+ role="menuitem"
179
+ onClick={() => {
180
+ onConnectorAction?.({ from: prevId, to: id, action: opt.id });
181
+ setConnectorPopover(null);
182
+ }}
183
+ >
184
+ {opt.label}
185
+ </button>
186
+ ))}
187
+ <button
188
+ type="button"
189
+ role="menuitem"
190
+ onClick={() => {
191
+ onConnectorAction?.({ action: "delete-edge-request", from: prevId, to: id });
192
+ setConnectorPopover(null);
193
+ }}
194
+ >
195
+ <Minus size={12} /> Delete edge
196
+ </button>
197
+ </div>
198
+ )}
199
+ </div>
200
+ )}
201
+ <div
202
+ role="button"
203
+ tabIndex={0}
204
+ className={`dm-orchestration-node${isSelected ? " dm-orchestration-node--selected" : ""}`}
205
+ title={hoverHint(node)}
206
+ onClick={() => {
207
+ setInternalSelected(id);
208
+ onSelectNode?.(node);
209
+ }}
210
+ onKeyDown={(event) => {
211
+ if (event.key !== "Enter" && event.key !== " ") return;
212
+ event.preventDefault();
213
+ setInternalSelected(id);
214
+ onSelectNode?.(node);
215
+ }}
216
+ >
217
+ <span className="dm-orchestration-node__icon" aria-hidden="true">
218
+ <Icon size={14} />
219
+ </span>
220
+ <span className="dm-orchestration-node__type">{NODE_TYPE_LABELS[node.type] || node.type}</span>
221
+ <span className="dm-orchestration-node__title">{normalizedNodeType(node)}</span>
222
+ <span className="dm-orchestration-node__gear" aria-hidden="true">
223
+ <Settings size={13} />
224
+ </span>
225
+ <span className="dm-orchestration-node__subtitle">{nodeSubtitle(node)}</span>
226
+ </div>
227
+ </div>
228
+ );
229
+ })}
230
+ </div>
231
+ {showRunTest && (
232
+ <div className="dm-orchestration-run-status">
233
+ <button type="button" className="dm-btn-primary-sm" onClick={onRunTest}>
234
+ Run sandbox
235
+ </button>
236
+ {runStatus && <span className={`dm-orchestration-run-status__badge is-${runStatus}`}>{runStatus}</span>}
237
+ {runMessage && <p className="dm-orchestration-run-status__message">{runMessage}</p>}
238
+ </div>
239
+ )}
240
+ </div>
241
+ );
242
+ }
@@ -0,0 +1,52 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ export function OrchestrationGraphEmptyCanvas({
6
+ onStartFromRegistry,
7
+ onStartBlank,
8
+ onPasteGraph,
9
+ disabled
10
+ }) {
11
+ const [showPaste, setShowPaste] = useState(false);
12
+ const [pasteText, setPasteText] = useState("");
13
+
14
+ return (
15
+ <div className="dm-orchestration-canvas dm-orchestration-canvas--empty-state" aria-label="Empty orchestration graph">
16
+ <div className="dm-orchestration-canvas__empty-card">
17
+ <h3>Start orchestration graph</h3>
18
+ <p>Create a governed run plan for this sandbox tool. Nothing executes until Run sandbox.</p>
19
+ <div className="dm-orchestration-canvas__empty-actions">
20
+ <button type="button" className="dm-btn-primary-sm" disabled={disabled} onClick={onStartFromRegistry}>
21
+ Start from API Registry
22
+ </button>
23
+ <button type="button" className="dm-btn-outline" disabled={disabled} onClick={onStartBlank}>
24
+ Start blank
25
+ </button>
26
+ </div>
27
+ <details
28
+ className="dm-orchestration-canvas__paste"
29
+ open={showPaste}
30
+ onToggle={(e) => setShowPaste(e.target.open)}
31
+ >
32
+ <summary>Paste graph JSON</summary>
33
+ <textarea
34
+ rows={6}
35
+ value={pasteText}
36
+ disabled={disabled}
37
+ placeholder='{"version":1,"provider":"growthub-native","nodes":[],"edges":[]}'
38
+ onChange={(e) => setPasteText(e.target.value)}
39
+ />
40
+ <button
41
+ type="button"
42
+ className="dm-btn-outline"
43
+ disabled={disabled || !pasteText.trim()}
44
+ onClick={() => onPasteGraph?.(pasteText)}
45
+ >
46
+ Apply pasted graph
47
+ </button>
48
+ </details>
49
+ </div>
50
+ </div>
51
+ );
52
+ }