@growthub/cli 0.14.1 → 0.14.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 (30) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +3 -2
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +3 -2
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +3 -2
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +14 -1
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +1 -1
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +49 -2
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +54 -11
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +113 -36
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +34 -14
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +7 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +13 -4
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +26 -0
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/local-intelligence-browser-access.js +516 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-agent-host.js +85 -7
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-process.js +3 -1
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/sandbox-adapter-registry.js +5 -1
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +1 -0
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +8 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +3 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +4 -2
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +1 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +82 -27
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +4 -2
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +1 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +6 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-swarm-proposal.js +3 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +364 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
  30. package/package.json +1 -1
@@ -20,9 +20,9 @@
20
20
  * no second product surface, no terminal emulator — just a uniform
21
21
  * readiness bridge.
22
22
  *
23
- * Status values stamped on the row are intentionally distinct between
24
- * confirmed-authenticated ("active") and merely-installed ("reachable")
25
- * so the pill cannot overclaim auth from a `--version` probe.
23
+ * The pill represents selected local host readiness. Legacy `reachable`
24
+ * metadata is rendered as Active so Data Model and workflow sidecars stay
25
+ * visually identical after a successful host switch/check path.
26
26
  */
27
27
 
28
28
  import { useCallback, useState } from "react";
@@ -32,7 +32,7 @@ import { getAgentHostCapabilities } from "@/lib/sandbox-agent-host-catalog";
32
32
 
33
33
  const STATUS_LABEL = {
34
34
  active: "Active",
35
- reachable: "Reachable",
35
+ reachable: "Active",
36
36
  stale: "Stale",
37
37
  missing: "Missing",
38
38
  checking: "Checking",
@@ -40,10 +40,9 @@ const STATUS_LABEL = {
40
40
  };
41
41
 
42
42
  function statusKind(status) {
43
- if (status === "active") return "ok";
43
+ if (status === "active" || status === "reachable") return "ok";
44
44
  if (status === "stale") return "warn";
45
45
  if (status === "missing") return "bad";
46
- // "reachable" stays neutral — CLI is installed, but auth is NOT confirmed.
47
46
  return "";
48
47
  }
49
48
 
@@ -62,15 +61,28 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
62
61
  const [busy, setBusy] = useState(null); // "status" | "login" | "logout" | null
63
62
  const [output, setOutput] = useState(null);
64
63
  const [message, setMessage] = useState("");
64
+ const [localAuthState, setLocalAuthState] = useState(null);
65
65
 
66
66
  const capabilities = getAgentHostCapabilities(draft);
67
67
  const providerMatchesHost = String(draft?.agentAuthProvider || "").trim() === String(draft?.agentHost || "").trim();
68
+ const localMatchesHost =
69
+ localAuthState?.provider &&
70
+ String(localAuthState.provider || "").trim() === String(capabilities?.slug || "").trim();
68
71
  const currentStatus =
69
- providerMatchesHost && typeof draft?.agentAuthStatus === "string" && draft.agentAuthStatus.trim()
70
- ? draft.agentAuthStatus.trim()
71
- : "unknown";
72
- const lastChecked = providerMatchesHost ? draft?.agentAuthLastChecked || "" : "";
73
- const lastMessage = providerMatchesHost ? draft?.agentAuthLastMessage || "" : "";
72
+ localMatchesHost && typeof localAuthState?.status === "string" && localAuthState.status.trim()
73
+ ? localAuthState.status.trim()
74
+ : providerMatchesHost && typeof draft?.agentAuthStatus === "string" && draft.agentAuthStatus.trim()
75
+ ? draft.agentAuthStatus.trim()
76
+ : "unknown";
77
+ const lastChecked = localMatchesHost && localAuthState?.checkedAt
78
+ ? localAuthState.checkedAt
79
+ : providerMatchesHost
80
+ ? draft?.agentAuthLastChecked || ""
81
+ : "";
82
+ const lastMessage =
83
+ localMatchesHost && typeof localAuthState?.message === "string"
84
+ ? localAuthState.message
85
+ : providerMatchesHost ? draft?.agentAuthLastMessage || "" : "";
74
86
  const displayMessage = normalizeAuthMessage(message || lastMessage, capabilities?.label)
75
87
  || (currentStatus === "unknown" ? "Run Check or Login to verify this local agent host." : "");
76
88
 
@@ -85,15 +97,23 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
85
97
  const res = await fetch(endpoint, {
86
98
  method: "POST",
87
99
  headers: { "content-type": "application/json" },
88
- body: JSON.stringify({ objectId, name: rowName })
100
+ body: JSON.stringify({ objectId, name: rowName, agentHost: capabilities.slug })
89
101
  });
90
102
  const payload = await res.json();
91
103
  setOutput(payload);
92
104
  setMessage(payload.message || (payload.ok ? "Done" : payload.error || "Failed"));
105
+ if (payload.status) {
106
+ setLocalAuthState({
107
+ status: payload.status,
108
+ provider: payload.provider || capabilities.slug,
109
+ checkedAt: payload.checkedAt || new Date().toISOString(),
110
+ message: payload.message || ""
111
+ });
112
+ }
93
113
  if (typeof onPatchDraft === "function" && payload.status) {
94
114
  onPatchDraft({
95
115
  agentAuthStatus: payload.status,
96
- agentAuthProvider: payload.provider || draft?.agentHost || "unknown",
116
+ agentAuthProvider: payload.provider || capabilities.slug,
97
117
  agentAuthLastChecked: payload.checkedAt || new Date().toISOString(),
98
118
  agentAuthLastExitCode:
99
119
  typeof payload.exitCode === "number" ? payload.exitCode : null,
@@ -107,7 +127,7 @@ export function SandboxAgentAuthPanel({ objectId, rowName, draft, disabled, onPa
107
127
  setBusy(null);
108
128
  }
109
129
  },
110
- [canAct, objectId, rowName, onPatchDraft, draft?.agentHost]
130
+ [canAct, objectId, rowName, onPatchDraft, capabilities?.slug]
111
131
  );
112
132
 
113
133
  const onCheckStatus = () =>
@@ -6253,9 +6253,16 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
6253
6253
  .dm-sandbox-config > .dm-cockpit { width: 100%; margin: 12px 0 8px; box-sizing: border-box; }
6254
6254
  .dm-radio-row { display: grid; gap: 8px; }
6255
6255
  .dm-radio-row label, .dm-check-row { display: grid; grid-template-columns: 16px minmax(0,1fr); align-items: start; column-gap: 8px; color: #1f2937; font-size: 12px; line-height: 1.35; }
6256
+ .dm-check-row-compact { grid-template-columns: 16px max-content 22px; align-items: center; width: fit-content; max-width: 100%; }
6256
6257
  .dm-radio-row input[type="radio"], .dm-check-row input[type="checkbox"] { width: 14px; height: 14px; margin: 1px 0 0; padding: 0; box-shadow: none; accent-color: #111827; }
6257
6258
  .dm-radio-row span, .dm-check-row span { color: #1f2937; font-size: 12px; font-weight: 500; }
6258
6259
  .dm-check-row { cursor: pointer; }
6260
+ .dm-check-row-compact > label { color: #1f2937; font-size: 12px; font-weight: 500; cursor: pointer; }
6261
+ .dm-help-wrap { position: relative; display: inline-flex; align-items: center; min-width: 0; }
6262
+ .dm-icon-help { width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 4px; background: transparent; color: #64748b; padding: 0; cursor: help; }
6263
+ .dm-icon-help:hover, .dm-icon-help:focus-visible { background: #f1f5f9; color: #334155; outline: none; }
6264
+ .dm-help-bubble { position: absolute; z-index: 80; left: 22px; top: 50%; transform: translateY(-50%); width: min(280px, calc(100vw - 48px)); display: none; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; box-shadow: 0 12px 30px rgba(15,23,42,.16); color: #334155; font-size: 11px; font-weight: 500; line-height: 1.4; padding: 8px 9px; }
6265
+ .dm-help-wrap:hover .dm-help-bubble, .dm-help-bubble.is-open { display: block; }
6259
6266
  .dm-select { position: relative; width: 100%; min-width: 0; font-size: 11px; }
6260
6267
  .dm-select-trigger { width: 100%; min-height: 32px; display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid #cbd5e1; border-radius: 7px; background: #fff; color: #111827; box-shadow: 0 1px 2px rgba(15,23,42,.05); font: inherit; font-size: 11px; padding: 6px 10px; text-align: left; cursor: pointer; transition: border-color .12s, box-shadow .12s, background .12s; }
6261
6268
  .dm-select-trigger:hover:not(:disabled) { border-color: #94a3b8; box-shadow: 0 2px 8px rgba(15,23,42,.08); }
@@ -912,6 +912,7 @@ export default function WorkflowSurface() {
912
912
  })
913
913
  : null;
914
914
  const isServerlessWorkflow = Boolean(serverlessState?.isServerless);
915
+ const showServerlessUpgrade = String(sandboxRow?.adapter || "").trim() !== "local-intelligence";
915
916
 
916
917
  async function patchSandboxAndPersist(fields) {
917
918
  if (resolved.rowIndex < 0 || !objectId || !workspaceConfig) return;
@@ -1013,7 +1014,7 @@ export default function WorkflowSurface() {
1013
1014
  >
1014
1015
  <ArrowUp size={13} />
1015
1016
  </button>
1016
- {sandboxRow && (
1017
+ {sandboxRow && showServerlessUpgrade && (
1017
1018
  <button
1018
1019
  type="button"
1019
1020
  className={"dm-workflow-icon-btn dm-workflow-upgrade-btn" + (isServerlessWorkflow ? " is-serverless" : (upgradeState.showOnboarding ? " is-pulse" : ""))}
@@ -1051,7 +1052,13 @@ export default function WorkflowSurface() {
1051
1052
  <Power size={13} /> {publishing ? "Publishing" : "Publish"}
1052
1053
  </button>
1053
1054
  )}
1054
- <button type="button" className="dm-workflow-chip-btn" disabled={!sandboxRow} onClick={openTraceMode}>
1055
+ <button
1056
+ type="button"
1057
+ className="dm-workflow-chip-btn"
1058
+ onClick={() => {
1059
+ if (sandboxRow) openTraceMode();
1060
+ }}
1061
+ >
1055
1062
  <History size={13} /> See Runs
1056
1063
  </button>
1057
1064
  {sidecarMode === "trace" && (
@@ -1093,7 +1100,7 @@ export default function WorkflowSurface() {
1093
1100
 
1094
1101
  {/* One-time serverless upgrade onboarding — shows only when the operator
1095
1102
  has workflows but none are serverless, and hasn't dismissed it. */}
1096
- {sandboxRow && !upgradeOpen && upgradeState.showOnboarding ? (
1103
+ {sandboxRow && showServerlessUpgrade && !upgradeOpen && upgradeState.showOnboarding ? (
1097
1104
  <div className="workspace-template-context-banner dm-workflow-upgrade-nudge" role="note">
1098
1105
  <div>
1099
1106
  <strong>{upgradeState.headline}</strong>
@@ -1111,7 +1118,7 @@ export default function WorkflowSurface() {
1111
1118
  {/* Serverless cockpit — same derivation + cockpit interface as the API
1112
1119
  Registry and sandbox lanes. Toggles patch the sandbox row; deep config
1113
1120
  (scheduler/adapter) routes to the object's Data Model drawer. */}
1114
- {sandboxRow && upgradeOpen && serverlessState ? (
1121
+ {sandboxRow && showServerlessUpgrade && upgradeOpen && serverlessState ? (
1115
1122
  <div className="dm-workflow-upgrade-panel">
1116
1123
  <div className="dm-workflow-upgrade-panel-head">
1117
1124
  <span className="dm-api-action-card-eyebrow">Persistence &amp; scheduling</span>
@@ -1233,6 +1240,8 @@ export default function WorkflowSurface() {
1233
1240
  graph={orchestrationGraph}
1234
1241
  objectId={objectId}
1235
1242
  rowName={rowId}
1243
+ sandboxRow={sandboxRow}
1244
+ onSandboxRowPatch={patchSandboxRuntimeFields}
1236
1245
  disabled={false}
1237
1246
  onGraphChange={(updater) => {
1238
1247
  setOrchestrationGraph((g) => (typeof updater === "function" ? updater(g) : updater));
@@ -23,6 +23,32 @@ Agents and streamed APIs elsewhere in the sandbox stay orthogonal: serverless sw
23
23
 
24
24
  Sandbox rows reference **`authRef` / named env refs** — never literals in browser or config records. Scheduling uses the referenced API Registry row’s **`authRef`** merge rules identical to **`/api/workspace/test-source`**.
25
25
 
26
+ ## Browser access (`browserAccess`)
27
+
28
+ `browserAccess` is a first-class boolean column on the sandbox row, surfaced as a single toggle in the record sidecar's **Environment & Network** section. It is locality-agnostic and agent-host-agnostic: the saved record carries the capability, and each execution path grants it through the mechanism that path actually understands.
29
+
30
+ **Deterministic normalization** — browser access implies outbound network. The sidecar toggle stamps `networkAllow: "true"` when browser access is switched on, and `POST /api/workspace/sandbox-run` enforces the same implication server-side (`networkAllow || browserAccess`), so rows patched via the API behave identically to rows saved in the UI.
31
+
32
+ **This is the product's existing agent browser primitive, surfaced — not a new system.** The upstream Paperclip server already grants any agent browser access through one boolean: the agent config's `chrome` primitive (`ui/src/components/agent-config-primitives.tsx` — "Enable Claude's Chrome integration by passing --chrome"), gated by the chrome-lease service (`server/src/services/chrome-lease.ts`) before `adapter.execute()`. The CMS profile contract likewise speaks `allowBrowserBridge` and execution mode `"browser"`. `browserAccess` is the same bit on the governed sandbox row, so rows stay portable to the upstream adapter registry without translation — exactly like the host slugs.
33
+
34
+ **Local (`local-agent-host`)** — when the row's bit is on, each host engages its **first-party** browser integration; the adapter never invents flags or writes host config it cannot verify against the upstream tool (the same rule the auth catalog follows for login subcommands):
35
+
36
+ | Lane | Hosts | Mechanism |
37
+ | --- | --- | --- |
38
+ | `native-flag` | Claude Code | `--chrome` — Claude's own Chrome integration, the same flag the upstream server adapter passes for the agent `chrome` primitive. |
39
+ | `native-flag` | Codex | `--enable browser_use --enable in_app_browser` (with `--sandbox workspace-write`). |
40
+ | `env-signal` | Cursor, Gemini, Qwen, OpenCode, Pi, Hermes, OpenClaw Gateway | The host receives `GROWTHUB_SANDBOX_BROWSER_ACCESS=1` (mirroring the upstream browser-isolation context); whatever browser integration the operator has configured in that host honors the row's setting. |
41
+
42
+ The lane engaged for a run is recorded in `adapterMeta.browserLane`, and the run-console record projection surfaces `context.browserAccess` plus the full `adapterMeta`, so every run shows its browser proof. No host-global config (`~/.claude`, `~/.codex`, …) is ever mutated.
43
+
44
+ **Orchestration graph** — this is why browser access is node-level and host-agnostic with zero extra configuration: `thinAdapter` and `ai-agent` nodes execute through this same host catalog, so every node inherits the row's browser grant no matter which host runs it (subagent nodes through the existing node-level Network gate; orchestrator and synthesis phases directly).
45
+
46
+ One deliberate decision, stated explicitly: **Codex `workspace-write` on `networkAllow` alone is intentional.** Codex's `read-only` sandbox blocks all outbound network, so `workspace-write` is the least-privileged Codex mode where the row's network grant can take effect — and writes are confined to the sealed ephemeral workdir the adapter spawns into, never the operator's repo. Browser flags remain gated on `browserAccess` only; network alone never opens a browser.
47
+
48
+ **Local (`local-process`)** and every other adapter — the sealed RunRequest carries `browserAccess: boolean`, and the env contract publishes `GROWTHUB_SANDBOX_BROWSER_ACCESS=1|0` alongside `GROWTHUB_SANDBOX_NET_ALLOW(LIST)`, so any script or drop-zone adapter honors the row's setting without knowing about specific hosts.
49
+
50
+ **Serverless** — the `growthub-sandbox-run-v1` envelope carries `sandbox.browserAccess` (plus `networkAllow` / `allowList`), so a workflow upgraded from local to serverless keeps the identical capability contract: the Edge/QStash/cron handler reads one boolean and grants its own runtime's browser (e.g. a remote browser pool or hosted agent's browser tool). No host-specific knowledge crosses the wire — slugs and booleans only, never secrets.
51
+
26
52
  ## Not a widget source
27
53
 
28
54
  Workspace Builder excludes **`sandbox-environment`** from View widget bindings (execution records, not tabular KPI sources). See **`data-sources-api-registry.md`** in this folder.