@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.
Files changed (42) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +24 -2
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +14 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +74 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +67 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +77 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +72 -4
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +123 -27
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +224 -1
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +754 -92
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +224 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +32 -1
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +530 -9
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +8 -1
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +10 -7
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/RunSetupPanel.jsx +261 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +119 -9
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +779 -138
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +91 -14
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +35 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +28 -3
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +216 -5
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +412 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +366 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +34 -3
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-eligibility.js +50 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-redaction.js +64 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +665 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-host-catalog.js +168 -0
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +595 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +164 -7
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +11 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
  40. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +111 -1
  41. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +14 -0
  42. package/package.json +1 -1
@@ -232,6 +232,50 @@ function filterNavFolderRows(rows, query, typeFilter) {
232
232
  });
233
233
  }
234
234
 
235
+ /**
236
+ * Derive which nav-folder item is "active" for the current route. The
237
+ * destination URLs here mirror `openDashboardItem`/`openViewItem`/
238
+ * `openWorkflowItem` below — so a freshly mounted rail can recover the
239
+ * active item (and therefore its parent folder) purely from
240
+ * pathname + searchParams, without depending on transient client state.
241
+ */
242
+ function isNavItemActive(item, pathname, searchParams) {
243
+ if (!item || !pathname) return false;
244
+ const get = (key) => (searchParams && typeof searchParams.get === "function"
245
+ ? searchParams.get(key)
246
+ : null);
247
+ if (item.type === "dashboard") {
248
+ return pathname === "/" && get("dashboard") === String(item.refId || "");
249
+ }
250
+ if (item.type === "view") {
251
+ return pathname.startsWith("/data-model")
252
+ && get("object") === String(item.objectId || "");
253
+ }
254
+ if (item.type === "workflow") {
255
+ return pathname.startsWith("/workflows")
256
+ && get("object") === String(item.objectId || "")
257
+ && get("row") === String(item.rowId || "");
258
+ }
259
+ return false;
260
+ }
261
+
262
+ /**
263
+ * Walk the persisted nav-folder rows and return the folder id whose item
264
+ * matches the active route, or null if none do. This is the anchor the
265
+ * rail uses to keep the parent folder open while the user navigates
266
+ * between its dashboards / views / workflows.
267
+ */
268
+ function deriveActiveNavFolderId(rows, pathname, searchParams) {
269
+ if (!Array.isArray(rows) || !rows.length) return null;
270
+ for (const folder of rows) {
271
+ const items = Array.isArray(folder?.items) ? folder.items : [];
272
+ for (const item of items) {
273
+ if (isNavItemActive(item, pathname, searchParams)) return folder.id;
274
+ }
275
+ }
276
+ return null;
277
+ }
278
+
235
279
  function hexToTintBg(hex, alpha = 0.1) {
236
280
  const h = String(hex || "").replace("#", "");
237
281
  if (!/^[0-9a-f]{6}$/i.test(h)) return "#f5f5f5";
@@ -415,6 +459,19 @@ function NavFoldersSection({
415
459
  const dashboards = useMemo(() => listAvailableDashboards(workspaceConfig), [workspaceConfig]);
416
460
  const viewableObjects = useMemo(() => listAvailableObjectsForViews(workspaceConfig), [workspaceConfig]);
417
461
  const workflows = useMemo(() => listAvailableWorkflows(workspaceConfig), [workspaceConfig]);
462
+ const activeFolderId = useMemo(
463
+ () => deriveActiveNavFolderId(rows, pathname, searchParams),
464
+ [rows, pathname, searchParams],
465
+ );
466
+
467
+ // When the user lands on a route owned by a folder item (deep-link,
468
+ // reload, cross-page navigation), expand the Folders section so the
469
+ // active folder/item is visible without an extra click. Manual
470
+ // collapse still wins — the effect only fires when activeFolderId
471
+ // changes, not on every render.
472
+ useEffect(() => {
473
+ if (activeFolderId) setSectionCollapsed(false);
474
+ }, [activeFolderId]);
418
475
  const filteredEntries = useMemo(
419
476
  () => filterNavFolderRows(rows, filterQuery, filterType),
420
477
  [rows, filterQuery, filterType],
@@ -875,7 +932,10 @@ function NavFoldersSection({
875
932
  type="button"
876
933
  role="menuitem"
877
934
  className="workspace-rail-thread-menu-item"
878
- onClick={() => startCustomizeItem(folder, item)}
935
+ onClick={(e) => {
936
+ e.stopPropagation();
937
+ startCustomizeItem(folder, item);
938
+ }}
879
939
  >
880
940
  <Pencil size={13} aria-hidden="true" /> Customize
881
941
  </button>
@@ -883,7 +943,10 @@ function NavFoldersSection({
883
943
  type="button"
884
944
  role="menuitem"
885
945
  className="workspace-rail-thread-menu-item is-destructive"
886
- onClick={() => deleteItem(folder.id, item.id)}
946
+ onClick={(e) => {
947
+ e.stopPropagation();
948
+ deleteItem(folder.id, item.id);
949
+ }}
887
950
  >
888
951
  <Trash2 size={13} aria-hidden="true" /> Remove
889
952
  </button>
@@ -896,11 +959,7 @@ function NavFoldersSection({
896
959
  const renderItemRow = (folder, item) => {
897
960
  const composedId = `${folder.id}::${item.id}`;
898
961
  const isMenuOpen = openMenuId === composedId;
899
- const isActive = item.type === "view" && pathname.startsWith(`/views/${encodeURIComponent(item.id)}`)
900
- || (item.type === "workflow"
901
- && pathname.startsWith("/workflows")
902
- && searchParams.get("object") === item.objectId
903
- && searchParams.get("row") === item.rowId);
962
+ const isActive = isNavItemActive(item, pathname, searchParams);
904
963
  const style = navItemStyle(item);
905
964
  const typeHint = item.type === "dashboard" ? "Dashboard" : item.type === "workflow" ? "Workflow" : "View";
906
965
  return (
@@ -918,7 +977,11 @@ function NavFoldersSection({
918
977
  <button
919
978
  type="button"
920
979
  className="workspace-rail-nav-row-main"
921
- onClick={() => {
980
+ onClick={(e) => {
981
+ // Child clicks must never reach the folder toggle — keep
982
+ // navigation independent from accordion state so the parent
983
+ // folder stays open while the user moves between its items.
984
+ e.stopPropagation();
922
985
  if (item.type === "dashboard") openDashboardItem(item);
923
986
  else if (item.type === "workflow") openWorkflowItem(item);
924
987
  else openViewItem(item);
@@ -996,7 +1059,10 @@ function NavFoldersSection({
996
1059
  type="button"
997
1060
  role="menuitem"
998
1061
  className="workspace-rail-thread-menu-item"
999
- onClick={() => startCustomizeFolder(folder)}
1062
+ onClick={(e) => {
1063
+ e.stopPropagation();
1064
+ startCustomizeFolder(folder);
1065
+ }}
1000
1066
  >
1001
1067
  <Pencil size={13} aria-hidden="true" /> Customize
1002
1068
  </button>
@@ -1005,7 +1071,8 @@ function NavFoldersSection({
1005
1071
  role="menuitem"
1006
1072
  className="workspace-rail-thread-menu-item"
1007
1073
  disabled={dashboards.length === 0}
1008
- onClick={() => {
1074
+ onClick={(e) => {
1075
+ e.stopPropagation();
1009
1076
  setOpenMenuId(null);
1010
1077
  setMenuAnchor(null);
1011
1078
  setAddPickerFor({ folderId: folder.id, kind: "dashboard" });
@@ -1018,7 +1085,8 @@ function NavFoldersSection({
1018
1085
  role="menuitem"
1019
1086
  className="workspace-rail-thread-menu-item"
1020
1087
  disabled={viewableObjects.length === 0}
1021
- onClick={() => {
1088
+ onClick={(e) => {
1089
+ e.stopPropagation();
1022
1090
  setOpenMenuId(null);
1023
1091
  setMenuAnchor(null);
1024
1092
  setAddPickerFor({ folderId: folder.id, kind: "view" });
@@ -1031,7 +1099,8 @@ function NavFoldersSection({
1031
1099
  role="menuitem"
1032
1100
  className="workspace-rail-thread-menu-item"
1033
1101
  disabled={workflows.length === 0}
1034
- onClick={() => {
1102
+ onClick={(e) => {
1103
+ e.stopPropagation();
1035
1104
  setOpenMenuId(null);
1036
1105
  setMenuAnchor(null);
1037
1106
  setAddPickerFor({ folderId: folder.id, kind: "workflow" });
@@ -1043,7 +1112,10 @@ function NavFoldersSection({
1043
1112
  type="button"
1044
1113
  role="menuitem"
1045
1114
  className="workspace-rail-thread-menu-item is-destructive"
1046
- onClick={() => deleteFolder(folder.id)}
1115
+ onClick={(e) => {
1116
+ e.stopPropagation();
1117
+ deleteFolder(folder.id);
1118
+ }}
1047
1119
  >
1048
1120
  <Trash2 size={13} aria-hidden="true" /> Delete
1049
1121
  </button>
@@ -1057,7 +1129,12 @@ function NavFoldersSection({
1057
1129
  const { folder, items, expand: forceExpand } = entry;
1058
1130
  const isMenuOpen = openMenuId === folder.id;
1059
1131
  const isCustomizing = customizeTarget?.scope === "folder" && customizeTarget.folderId === folder.id;
1060
- const collapsed = Boolean(folder.collapsed) && !forceExpand;
1132
+ // Active-folder preservation: if a child item matches the current
1133
+ // route, the parent folder stays open regardless of its persisted
1134
+ // `collapsed` flag — so navigating between sibling items inside a
1135
+ // folder never collapses the folder underneath the user.
1136
+ const isActiveFolder = activeFolderId === folder.id;
1137
+ const collapsed = !isActiveFolder && Boolean(folder.collapsed) && !forceExpand;
1061
1138
  const isExpanded = !collapsed;
1062
1139
  const style = navFolderStyle(folder);
1063
1140
  const visibleItems = items;
@@ -30,3 +30,38 @@ Workspace Builder excludes **`sandbox-environment`** from View widget bindings (
30
30
  ## Extension points
31
31
 
32
32
  - Custom adapters: `apps/workspace/lib/adapters/sandboxes/adapters/` (see `README.md` there).
33
+
34
+ ## Local agent auth onboarding
35
+
36
+ Sandbox rows whose **`adapter` is `local-agent-host`** route execution through whichever local agent CLI is registered for `agentHost` (Claude Code, Codex, Cursor, Gemini, OpenCode, Pi, Qwen, Hermes, OpenClaw Gateway). The record sidecar exposes a **uniform** auth onboarding panel beside the existing **Run sandbox** bar — the mental model is identical for every host:
37
+
38
+ 1. **Check status** — probes the host CLI for reachability and (where the catalog declares one) a real auth-status subcommand.
39
+ 2. **Run login** — only shown when the host catalog declares a documented `loginCommand`. Spawns it server-side and surfaces stdout / stderr / login URL.
40
+ 3. **Log out** — only shown when the host catalog declares a documented `logoutCommand`.
41
+ 4. **Run sandbox** — existing button (unchanged execution path).
42
+
43
+ Per-host capabilities (binary path, install hint, login/logout subcommands, notes for hosts without a documented login flow) are declared in **`apps/workspace/lib/sandbox-agent-host-catalog.js`**. Adding a new host means adding one entry there — never extending the auth helper or the panel component. The catalog is the single source of truth for "what does this host's onboarding look like?".
44
+
45
+ Wired through `POST /api/workspace/sandbox-agent-auth/{status,login,logout}` and the helper at `apps/workspace/lib/sandbox-agent-auth.js`. The Claude flow mirrors the upstream Paperclip server route in `server/src/routes/agents.ts` (`claude auth login` / `claude auth logout`) so behaviour matches the hosted agents surface.
46
+
47
+ ### Status semantics — uniform across every host
48
+
49
+ | Status | Meaning |
50
+ | ----------- | -------------------------------------------------------------------------------- |
51
+ | `active` | A real auth probe confirmed authentication (login exit 0, or `auth status` exit 0). |
52
+ | `reachable` | The CLI is callable (`--version` exit 0) — but authentication is **not** yet confirmed. |
53
+ | `stale` | The CLI is reachable but printed auth-shaped failure output. |
54
+ | `missing` | The binary is not on PATH. |
55
+ | `checking` | Transient UI state during a probe. |
56
+ | `unknown` | Indeterminate. |
57
+
58
+ A `--version` (or equivalent reachability) probe **never** promotes to `active`. The next sandbox-run is the source of truth for session readiness.
59
+
60
+ ### Authority invariants
61
+
62
+ - Auth setup is **separate** from the `local-agent-host` execution adapter — the adapter at `lib/adapters/sandboxes/default-local-agent-host.js` stays execution-only and does not mutate any host config file.
63
+ - Raw host tokens **never** enter `growthub.config.json`. Each CLI manages its own on-disk auth state. The sandbox row stores only safe metadata: `agentAuthStatus`, `agentAuthProvider`, `agentAuthLastChecked`, `agentAuthLastExitCode`, `agentAuthLastMessage`, `agentAuthLastLoginUrl`.
64
+ - Token-shaped output (`sk-ant-…`, `sk-…`, JWT, `Bearer …`, prefix patterns like `access_token=`, `api_key=`) is redacted server-side before crossing the response boundary.
65
+ - The schema rejects out-of-band PATCHes that try to stash a secret field (`token`, `apiKey`, `accessToken`, `refreshToken`, `bearer`, `password`, `secret`, `sessionKey`) on a sandbox row.
66
+ - The panel is hidden when `runLocality === "serverless"` (the local CLI is irrelevant in that case), when `adapter !== "local-agent-host"`, or when `agentHost` is not registered in the host auth catalog.
67
+ - Hosts without a documented login subcommand show only the **Check status** button plus the catalog's `notes` line directing the operator to sign in via the host CLI directly. No invented subcommands.