@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
@@ -23,7 +23,7 @@
23
23
  * Data Sources downstream can normalize either locality.
24
24
  *
25
25
  * Request body:
26
- * { objectId: string, name: string }
26
+ * { objectId: string, name: string, useDraft?: boolean, draftGraph?: string | object }
27
27
  *
28
28
  * Response (success):
29
29
  * {
@@ -75,6 +75,8 @@ import {
75
75
  ensureSandboxAdaptersLoaded,
76
76
  getSandboxAdapter
77
77
  } from "@/lib/adapters/sandboxes";
78
+ import { runOrchestrationGraphIfPresent } from "@/lib/orchestration-graph-runner";
79
+ import { parseOrchestrationGraph } from "@/lib/orchestration-graph";
78
80
 
79
81
  function envKeyCandidates(ref) {
80
82
  const token = String(ref || "")
@@ -425,6 +427,7 @@ async function POST(request) {
425
427
 
426
428
  const objectId = typeof body?.objectId === "string" ? body.objectId.trim() : "";
427
429
  const name = typeof body?.name === "string" ? body.name.trim() : "";
430
+ const useDraft = body?.useDraft === true;
428
431
  if (!objectId || !name) {
429
432
  return NextResponse.json({ ok: false, error: "objectId and name are required" }, { status: 400 });
430
433
  }
@@ -438,16 +441,23 @@ async function POST(request) {
438
441
  return NextResponse.json({ ok: false, error: `no sandbox row named ${name} in object ${objectId}` }, { status: 404 });
439
442
  }
440
443
 
441
- const runLocality = normalizeRunLocality(row);
442
- const runtime = KNOWN_SANDBOX_RUNTIMES.includes(row.runtime) ? row.runtime : "node";
443
- let adapterId = (typeof row.adapter === "string" && row.adapter.trim()) ? row.adapter.trim() : DEFAULT_SANDBOX_ADAPTER;
444
- const agentHost = typeof row.agentHost === "string" ? row.agentHost.trim() : "";
445
- const schedulerRegistryId = typeof row.schedulerRegistryId === "string" ? row.schedulerRegistryId.trim() : "";
446
- const networkAllow = coerceBoolean(row.networkAllow);
447
- const allowList = parseSandboxAllowList(row.allowList);
448
- const envRefSlugs = parseSandboxEnvRefs(row.envRefs);
449
- const command = typeof row.command === "string" ? row.command : "";
450
- const instructions = typeof row.instructions === "string" ? row.instructions.trim() : "";
444
+ const draftGraph = useDraft
445
+ ? parseOrchestrationGraph(body?.draftGraph || row.orchestrationDraftConfig || row.orchestrationDraftGraph)
446
+ : null;
447
+ const rowForRun = draftGraph
448
+ ? { ...row, orchestrationGraph: draftGraph, orchestrationConfig: draftGraph }
449
+ : row;
450
+
451
+ const runLocality = normalizeRunLocality(rowForRun);
452
+ const runtime = KNOWN_SANDBOX_RUNTIMES.includes(rowForRun.runtime) ? rowForRun.runtime : "node";
453
+ let adapterId = (typeof rowForRun.adapter === "string" && rowForRun.adapter.trim()) ? rowForRun.adapter.trim() : DEFAULT_SANDBOX_ADAPTER;
454
+ const agentHost = typeof rowForRun.agentHost === "string" ? rowForRun.agentHost.trim() : "";
455
+ const schedulerRegistryId = typeof rowForRun.schedulerRegistryId === "string" ? rowForRun.schedulerRegistryId.trim() : "";
456
+ const networkAllow = coerceBoolean(rowForRun.networkAllow);
457
+ const allowList = parseSandboxAllowList(rowForRun.allowList);
458
+ const envRefSlugs = parseSandboxEnvRefs(rowForRun.envRefs);
459
+ const command = typeof rowForRun.command === "string" ? rowForRun.command : "";
460
+ const instructions = typeof rowForRun.instructions === "string" ? rowForRun.instructions.trim() : "";
451
461
  const agentCommand = instructions
452
462
  ? `Instructions:\n${instructions}\n\nPrompt:\n${command}`
453
463
  : command;
@@ -455,17 +465,17 @@ async function POST(request) {
455
465
  adapterId === "local-intelligence"
456
466
  ? {
457
467
  userIntent: agentCommand,
458
- localModel: typeof row.localModel === "string" ? row.localModel.trim() : "",
459
- localEndpoint: typeof row.localEndpoint === "string" ? row.localEndpoint.trim() : "",
468
+ localModel: typeof rowForRun.localModel === "string" ? rowForRun.localModel.trim() : "",
469
+ localEndpoint: typeof rowForRun.localEndpoint === "string" ? rowForRun.localEndpoint.trim() : "",
460
470
  intelligenceAdapterMode:
461
- typeof row.intelligenceAdapterMode === "string"
462
- ? row.intelligenceAdapterMode.trim().toLowerCase()
471
+ typeof rowForRun.intelligenceAdapterMode === "string"
472
+ ? rowForRun.intelligenceAdapterMode.trim().toLowerCase()
463
473
  : "ollama",
464
474
  }
465
475
  : undefined;
466
- const lifecycleStatus = String(row.lifecycleStatus || "draft").trim().toLowerCase() === "live" ? "live" : "draft";
467
- const version = row.version ?? "";
468
- const requestedTimeout = Number(row.timeoutMs);
476
+ const lifecycleStatus = String(rowForRun.lifecycleStatus || "draft").trim().toLowerCase() === "live" ? "live" : "draft";
477
+ const version = rowForRun.version ?? "";
478
+ const requestedTimeout = Number(rowForRun.timeoutMs);
469
479
  const timeoutMs = Number.isFinite(requestedTimeout) && requestedTimeout > 0
470
480
  ? Math.min(requestedTimeout, SANDBOX_MAX_TIMEOUT_MS)
471
481
  : SANDBOX_DEFAULT_TIMEOUT_MS;
@@ -503,16 +513,25 @@ async function POST(request) {
503
513
  let result;
504
514
  let effectiveAdapterId = adapterId;
505
515
 
506
- if (runLocality === "serverless") {
516
+ const hasNativeGraph = Boolean(parseOrchestrationGraph(rowForRun.orchestrationGraph || rowForRun.orchestrationConfig));
517
+ if (hasNativeGraph && runLocality !== "serverless") {
518
+ const graphResult = await runOrchestrationGraphIfPresent({ workspaceConfig, row: rowForRun, timeoutMs });
519
+ if (graphResult !== null) {
520
+ result = graphResult;
521
+ effectiveAdapterId = "orchestration-graph";
522
+ }
523
+ }
524
+
525
+ if (!result && runLocality === "serverless") {
507
526
  effectiveAdapterId = "serverless";
508
527
  result = await runServerlessScheduler({
509
528
  workspaceConfig,
510
- row,
529
+ row: rowForRun,
511
530
  runId,
512
531
  ranAt,
513
532
  workspaceId: workspaceConfig?.id ?? null,
514
533
  objectId,
515
- sandboxName: row.Name || name,
534
+ sandboxName: rowForRun.Name || name,
516
535
  runtime,
517
536
  adapterId,
518
537
  agentHost,
@@ -525,7 +544,7 @@ async function POST(request) {
525
544
  envRefsResolved,
526
545
  envRefsMissing
527
546
  });
528
- } else {
547
+ } else if (!result) {
529
548
  await ensureSandboxAdaptersLoaded();
530
549
  const adapter = getSandboxAdapter(adapterId);
531
550
  if (!adapter) {
@@ -547,7 +566,7 @@ async function POST(request) {
547
566
  try {
548
567
  result = await adapter.run({
549
568
  runId,
550
- name: row.Name || name,
569
+ name: rowForRun.Name || name,
551
570
  runtime,
552
571
  agentHost,
553
572
  command: adapterId === "local-agent-host" || adapterId === "local-intelligence" ? agentCommand : command,
@@ -594,7 +613,7 @@ async function POST(request) {
594
613
  allowList,
595
614
  result,
596
615
  timeoutMs,
597
- row
616
+ row: rowForRun
598
617
  });
599
618
 
600
619
  const sourceId = sandboxRunSourceId(objectId, row.Name || name);
@@ -633,7 +652,13 @@ async function POST(request) {
633
652
  lastTested: ranAt,
634
653
  lastRunId: runId,
635
654
  lastSourceId: sourceIdValue,
636
- lastResponse: compactResponse
655
+ lastResponse: compactResponse,
656
+ ...(useDraft ? {
657
+ orchestrationDraftLastTested: ranAt,
658
+ orchestrationDraftLastRunId: runId,
659
+ orchestrationDraftLastStatus: status,
660
+ orchestrationDraftLastResponse: compactResponse
661
+ } : {})
637
662
  };
638
663
  });
639
664
  return { ...entry, rows: nextRows };
@@ -0,0 +1,141 @@
1
+ "use client";
2
+
3
+ import { Check, ExternalLink, Play, Terminal, X } from "lucide-react";
4
+ import { getApiRegistrySandboxToolState } from "@/lib/orchestration-graph";
5
+
6
+ export function ApiRegistryActionCard({
7
+ registryRow,
8
+ workspaceConfig,
9
+ disabled,
10
+ onCreateSandboxTool,
11
+ onOpenSandboxTool,
12
+ onRunSandboxTool,
13
+ onTestConnection,
14
+ testing,
15
+ sandboxRunning
16
+ }) {
17
+ const state = getApiRegistrySandboxToolState(registryRow, workspaceConfig);
18
+
19
+ if (state.kind === "incomplete") {
20
+ const checklist = state.checklist || [];
21
+ return (
22
+ <section className="dm-api-action-card dm-api-action-card-muted" aria-label="Complete API setup">
23
+ <div className="dm-api-action-card-body">
24
+ <p className="dm-api-action-card-eyebrow">API Registry</p>
25
+ <h3>Complete API setup</h3>
26
+ <p>
27
+ This API needs a registry ID, base URL, endpoint, method, and auth reference before it can become a sandbox tool.
28
+ </p>
29
+ <ul className="dm-api-action-checklist">
30
+ {checklist.map((item) => (
31
+ <li key={item.field} className={item.ok ? "is-done" : "is-pending"}>
32
+ {item.ok ? <Check size={14} aria-hidden="true" /> : <X size={14} aria-hidden="true" />}
33
+ <span>{item.field}</span>
34
+ </li>
35
+ ))}
36
+ </ul>
37
+ </div>
38
+ </section>
39
+ );
40
+ }
41
+
42
+ if (state.kind === "untested") {
43
+ return (
44
+ <section className="dm-api-action-card dm-api-action-card-muted" aria-label="Test API first">
45
+ <div className="dm-api-action-card-body">
46
+ <p className="dm-api-action-card-eyebrow">Sandbox tool</p>
47
+ <h3>Test this API first</h3>
48
+ <p>{state.message}</p>
49
+ </div>
50
+ <button
51
+ type="button"
52
+ className="dm-btn-primary-sm dm-api-action-card-cta"
53
+ disabled={disabled || testing}
54
+ onClick={onTestConnection}
55
+ >
56
+ {testing ? "Testing…" : "Test connection"}
57
+ </button>
58
+ </section>
59
+ );
60
+ }
61
+
62
+ if (state.kind === "failed") {
63
+ return (
64
+ <section className="dm-api-action-card dm-api-action-card-muted" aria-label="API test failed">
65
+ <div className="dm-api-action-card-body">
66
+ <p className="dm-api-action-card-eyebrow">Connection</p>
67
+ <h3>API test failed</h3>
68
+ <p>{state.message}</p>
69
+ </div>
70
+ <button
71
+ type="button"
72
+ className="dm-btn-outline dm-api-action-card-cta"
73
+ disabled={disabled || testing}
74
+ onClick={onTestConnection}
75
+ >
76
+ {testing ? "Testing…" : "Retest"}
77
+ </button>
78
+ </section>
79
+ );
80
+ }
81
+
82
+ if (state.kind === "existing") {
83
+ const toolName = String(state.row?.Name || "").trim();
84
+ return (
85
+ <section className="dm-api-action-card" aria-label="Sandbox tool ready">
86
+ <div className="dm-api-action-card-icon" aria-hidden="true">
87
+ <Terminal size={18} />
88
+ </div>
89
+ <div className="dm-api-action-card-body">
90
+ <p className="dm-api-action-card-eyebrow">Sandbox tool</p>
91
+ <h3>Sandbox tool ready</h3>
92
+ <p>{toolName} — governed row linked to this API Registry entry.</p>
93
+ </div>
94
+ <div className="dm-api-action-card-actions">
95
+ <button
96
+ type="button"
97
+ className="dm-btn-outline dm-api-action-card-cta"
98
+ disabled={disabled || !toolName}
99
+ onClick={() => onOpenSandboxTool?.({ name: toolName })}
100
+ >
101
+ <ExternalLink size={14} aria-hidden="true" />
102
+ Open sandbox tool
103
+ </button>
104
+ <button
105
+ type="button"
106
+ className="dm-btn-primary-sm dm-api-action-card-cta"
107
+ disabled={disabled || sandboxRunning || !toolName}
108
+ onClick={() => onRunSandboxTool?.({ name: toolName })}
109
+ >
110
+ <Play size={14} aria-hidden="true" />
111
+ {sandboxRunning ? "Running…" : "Run sandbox"}
112
+ </button>
113
+ </div>
114
+ </section>
115
+ );
116
+ }
117
+
118
+ return (
119
+ <section className="dm-api-action-card" aria-label="Create sandbox tool">
120
+ <div className="dm-api-action-card-icon" aria-hidden="true">
121
+ <Terminal size={18} />
122
+ </div>
123
+ <div className="dm-api-action-card-body">
124
+ <p className="dm-api-action-card-eyebrow">API connected</p>
125
+ <h3>Create sandbox tool</h3>
126
+ <p>
127
+ This API is connected. Turn it into a sandbox tool that agents can run safely from this workspace.
128
+ </p>
129
+ <p className="dm-api-action-card-note">No secrets are stored. Nothing runs until you test the sandbox.</p>
130
+ </div>
131
+ <button
132
+ type="button"
133
+ className="dm-btn-primary-sm dm-api-action-card-cta"
134
+ disabled={disabled}
135
+ onClick={onCreateSandboxTool}
136
+ >
137
+ Create sandbox tool
138
+ </button>
139
+ </section>
140
+ );
141
+ }
@@ -0,0 +1,38 @@
1
+ "use client";
2
+
3
+ import { CheckCircle2, X } from "lucide-react";
4
+
5
+ /**
6
+ * Read-only summary of a successfully tested API Registry row.
7
+ * Shown at the top of the sandbox-tool draft flow (not a blocking modal).
8
+ */
9
+ export function ApiRegistryReviewModal({ registryRow, onClose = null }) {
10
+ if (!registryRow) return null;
11
+ const integrationId = String(registryRow.integrationId || "").trim();
12
+ const endpoint = String(registryRow.endpoint || "").trim();
13
+ const method = String(registryRow.method || "GET").trim().toUpperCase();
14
+ const baseUrl = String(registryRow.baseUrl || "").trim();
15
+
16
+ return (
17
+ <section className="dm-api-review-banner" aria-label="Connected API summary">
18
+ <div className="dm-api-review-banner-icon" aria-hidden="true">
19
+ <CheckCircle2 size={18} />
20
+ </div>
21
+ <div className="dm-api-review-banner-body">
22
+ <p className="dm-api-review-banner-eyebrow">Connected API</p>
23
+ <h3>{registryRow.Name || integrationId}</h3>
24
+ <p>
25
+ This endpoint returned a valid response. You can now turn it into a sandbox tool.
26
+ </p>
27
+ <code className="dm-api-review-banner-route">
28
+ {method} {baseUrl && endpoint ? `${baseUrl.replace(/\/+$/, "")}/${endpoint.replace(/^\/+/, "")}` : endpoint || baseUrl}
29
+ </code>
30
+ </div>
31
+ {onClose && (
32
+ <button type="button" className="dm-sidebar-close" onClick={onClose} aria-label="Dismiss summary">
33
+ <X size={16} />
34
+ </button>
35
+ )}
36
+ </section>
37
+ );
38
+ }