@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
@@ -0,0 +1,412 @@
1
+ /**
2
+ * Live Runs Console model layer.
3
+ *
4
+ * Pure, framework-free transformation of sandbox run records (saved under
5
+ * `growthub.source-records.json` and `row.lastResponse`) into the shape the
6
+ * Live Runs Console UI consumes: lifecycle, log tree, timeline, search, and
7
+ * a redacted JSON bundle that can be downloaded client-side.
8
+ *
9
+ * This module deliberately does NOT import React, does NOT call fetch, and
10
+ * does NOT mutate workspace config. It is the seam between the AWaC run
11
+ * substrate (sandbox-run route + source records) and the observability UI.
12
+ *
13
+ * See:
14
+ * - app/api/workspace/sandbox-run/route.js (writes records this consumes)
15
+ * - app/data-model/components/OrchestrationRunTracePanel.jsx (consumer)
16
+ * - lib/orchestration-run-trace.js (lower-level record parser)
17
+ */
18
+
19
+ import { redactSecretsFromText } from "./orchestration-graph.js";
20
+ import { redactRunInputsEnvelope, summarizeRunInputs } from "./orchestration-run-inputs.js";
21
+
22
+ const RUN_LOG_BUNDLE_KIND = "growthub-sandbox-run-log-v1";
23
+ const DEFAULT_EXPORT_TARGETS = Object.freeze([
24
+ "download-json",
25
+ "copy-output",
26
+ "download-stdout",
27
+ "download-stderr",
28
+ "download-normalized-output",
29
+ "download-log-node"
30
+ ]);
31
+
32
+ function safeString(value) {
33
+ if (value == null) return "";
34
+ return typeof value === "string" ? value : String(value);
35
+ }
36
+
37
+ function safeJsonString(value) {
38
+ if (value == null) return "";
39
+ if (typeof value === "string") return value;
40
+ try {
41
+ return JSON.stringify(value, null, 2);
42
+ } catch {
43
+ return "";
44
+ }
45
+ }
46
+
47
+ function parseDateMs(value) {
48
+ const text = safeString(value).trim();
49
+ if (!text) return null;
50
+ const ms = Date.parse(text);
51
+ return Number.isFinite(ms) ? ms : null;
52
+ }
53
+
54
+ function clampNumber(value) {
55
+ const n = Number(value);
56
+ return Number.isFinite(n) ? n : null;
57
+ }
58
+
59
+ function deriveRunSummary(record) {
60
+ if (!record || typeof record !== "object") {
61
+ return { status: "unknown", ok: false, label: "unknown" };
62
+ }
63
+ const exitCode = clampNumber(record.exitCode);
64
+ const errorText = safeString(record.error).trim();
65
+ const httpStatus = clampNumber(record?.adapterMeta?.httpStatus);
66
+ const aborted = record?.adapterMeta?.aborted === true;
67
+ let status = "unknown";
68
+ let ok = false;
69
+ if (aborted) {
70
+ status = "canceled";
71
+ } else if (exitCode === 0 && !errorText && (httpStatus == null || (httpStatus >= 200 && httpStatus < 300))) {
72
+ status = "completed";
73
+ ok = true;
74
+ } else if (errorText || (exitCode != null && exitCode !== 0)) {
75
+ status = "failed";
76
+ } else if (record?.lifecycleStatus === "executing" || record?.lifecycleStatus === "queued") {
77
+ status = String(record.lifecycleStatus);
78
+ }
79
+ return { status, ok, label: status };
80
+ }
81
+
82
+ function deriveRunLifecycle(record) {
83
+ if (!record || typeof record !== "object") return [];
84
+ const ranAtMs = parseDateMs(record.ranAt);
85
+ const durationMs = clampNumber(record.durationMs) || 0;
86
+ const finishedAtMs = ranAtMs != null ? ranAtMs + durationMs : null;
87
+ const lifecycle = [];
88
+ if (ranAtMs != null) {
89
+ lifecycle.push({ label: "Triggered", at: new Date(ranAtMs).toISOString(), durationMs: 0 });
90
+ lifecycle.push({ label: "Dequeued", at: new Date(ranAtMs).toISOString(), durationMs: 0 });
91
+ lifecycle.push({ label: "Started", at: new Date(ranAtMs).toISOString(), durationMs });
92
+ if (finishedAtMs != null) {
93
+ lifecycle.push({ label: "Finished", at: new Date(finishedAtMs).toISOString(), durationMs: 0 });
94
+ }
95
+ }
96
+ return lifecycle;
97
+ }
98
+
99
+ function buildLogChildren(record, summary) {
100
+ const children = [];
101
+ const stdout = safeString(record?.stdout).trim();
102
+ const stderr = safeString(record?.stderr).trim();
103
+ const errorText = safeString(record?.error).trim();
104
+ const output = safeJsonString(record?.output ?? record?.normalizedOutput ?? record?.response).trim();
105
+ const adapterMeta = record?.adapterMeta;
106
+ const durationMs = clampNumber(record?.durationMs) || 0;
107
+
108
+ if (errorText) {
109
+ children.push({
110
+ id: "error",
111
+ label: "error",
112
+ type: "error",
113
+ status: "failed",
114
+ durationMs: 0,
115
+ text: redactSecretsFromText(errorText)
116
+ });
117
+ }
118
+ if (stdout) {
119
+ children.push({
120
+ id: "stdout",
121
+ label: "stdout",
122
+ type: "stream",
123
+ status: "info",
124
+ durationMs,
125
+ text: redactSecretsFromText(stdout)
126
+ });
127
+ }
128
+ if (stderr) {
129
+ children.push({
130
+ id: "stderr",
131
+ label: "stderr",
132
+ type: "stream",
133
+ status: "failed",
134
+ durationMs: 0,
135
+ text: redactSecretsFromText(stderr)
136
+ });
137
+ }
138
+ if (output && output !== stdout) {
139
+ children.push({
140
+ id: "normalized-output",
141
+ label: "normalized output",
142
+ type: "output",
143
+ status: "info",
144
+ durationMs: 0,
145
+ text: redactSecretsFromText(output)
146
+ });
147
+ }
148
+ if (adapterMeta && typeof adapterMeta === "object") {
149
+ children.push({
150
+ id: "adapter-meta",
151
+ label: "adapter meta",
152
+ type: "meta",
153
+ status: "info",
154
+ durationMs: 0,
155
+ text: redactSecretsFromText(safeJsonString(adapterMeta))
156
+ });
157
+ }
158
+ return children;
159
+ }
160
+
161
+ function buildRunLogTree(record) {
162
+ if (!record || typeof record !== "object") return [];
163
+ if (Array.isArray(record.logTree) && record.logTree.length > 0) {
164
+ return record.logTree;
165
+ }
166
+ const summary = deriveRunSummary(record);
167
+ const durationMs = clampNumber(record?.durationMs) || 0;
168
+ const attemptChildren = buildLogChildren(record, summary);
169
+ const attemptNode = {
170
+ id: "attempt-1",
171
+ label: "Attempt 1",
172
+ type: "attempt",
173
+ status: summary.status,
174
+ durationMs,
175
+ children: attemptChildren
176
+ };
177
+ const rootNode = {
178
+ id: "root",
179
+ label: safeString(record?.adapter || "agent-run").trim() || "agent-run",
180
+ type: "root",
181
+ status: summary.status,
182
+ durationMs,
183
+ children: [attemptNode]
184
+ };
185
+ return [rootNode];
186
+ }
187
+
188
+ function buildExportsForRecord(record, stdoutText, stderrText, outputText) {
189
+ const declared = record?.exports?.available;
190
+ if (Array.isArray(declared) && declared.length > 0) {
191
+ return {
192
+ available: declared.map((id) => safeString(id).trim()).filter(Boolean),
193
+ external: Array.isArray(record?.exports?.external) ? record.exports.external.slice() : []
194
+ };
195
+ }
196
+ const available = ["download-json"];
197
+ if (stdoutText || outputText) available.push("copy-output");
198
+ if (stdoutText) available.push("download-stdout");
199
+ if (stderrText) available.push("download-stderr");
200
+ if (outputText && outputText !== stdoutText) available.push("download-normalized-output");
201
+ available.push("download-log-node");
202
+ return { available, external: [] };
203
+ }
204
+
205
+ function normalizeRunConsoleRecord(record) {
206
+ if (!record || typeof record !== "object") return null;
207
+ const summary = deriveRunSummary(record);
208
+ const lifecycle = deriveRunLifecycle(record);
209
+ const ranAtMs = parseDateMs(record.ranAt);
210
+ const durationMs = clampNumber(record.durationMs);
211
+ const finishedAt = ranAtMs != null && durationMs != null
212
+ ? new Date(ranAtMs + durationMs).toISOString()
213
+ : "";
214
+ const adapterMeta = record?.adapterMeta && typeof record.adapterMeta === "object"
215
+ ? record.adapterMeta
216
+ : null;
217
+ const templateTrace = record?.templateTrace && typeof record.templateTrace === "object"
218
+ ? record.templateTrace
219
+ : null;
220
+
221
+ const stdoutText = safeString(typeof record.stdout === "string" ? record.stdout : safeJsonString(record.stdout));
222
+ const stderrText = safeString(record.stderr);
223
+ const errorText = safeString(record.error);
224
+ const outputRaw = record.output ?? record.normalizedOutput ?? record.response;
225
+ const outputText = typeof outputRaw === "string" ? outputRaw : safeJsonString(outputRaw);
226
+ const rawInput = record.input || record.runInputs || null;
227
+ const safeInput = rawInput ? redactRunInputsEnvelope(rawInput) : null;
228
+ const inputSummary = safeInput ? summarizeRunInputs(safeInput) : null;
229
+ const exports = buildExportsForRecord(record, stdoutText, stderrText, outputText);
230
+ // Workspace Metadata Graph V1 — safe lineage projection. Names only,
231
+ // no secrets. Lets the Live Runs Console UI render "this run came from
232
+ // sandbox X / workflow Y / adapter Z / agent host A" without re-deriving
233
+ // the relationships from raw fields.
234
+ const lineage = {
235
+ runId: safeString(record.runId).trim(),
236
+ objectId: safeString(record.objectId).trim(),
237
+ sandboxName: safeString(record.name || record.sandboxName).trim(),
238
+ workflowRowId: safeString(record.name || record.sandboxName).trim(),
239
+ workflowMetadataId: safeString(record.objectId).trim() && safeString(record.name || record.sandboxName).trim()
240
+ ? `workflow:${safeString(record.objectId).trim()}:${safeString(record.name || record.sandboxName).trim()}`
241
+ : "",
242
+ sandboxMetadataId: safeString(record.objectId).trim() && safeString(record.name || record.sandboxName).trim()
243
+ ? `sandbox:${safeString(record.objectId).trim()}:${safeString(record.name || record.sandboxName).trim()}`
244
+ : "",
245
+ adapter: safeString(record.adapter).trim(),
246
+ agentHost: safeString(record.agentHost).trim(),
247
+ runtime: safeString(record.runtime).trim(),
248
+ runLocality: safeString(record.runLocality).trim(),
249
+ inputFieldCount: inputSummary ? inputSummary.fieldCount : 0,
250
+ inputSource: inputSummary ? inputSummary.source : "",
251
+ hasOutput: Boolean(outputText)
252
+ };
253
+
254
+ return {
255
+ runId: safeString(record.runId).trim(),
256
+ status: summary.status,
257
+ ok: summary.ok,
258
+ exitCode: clampNumber(record.exitCode),
259
+ ranAt: safeString(record.ranAt).trim(),
260
+ finishedAt,
261
+ durationMs: durationMs == null ? null : durationMs,
262
+ queueMs: 0,
263
+ runtime: safeString(record.runtime).trim(),
264
+ adapter: safeString(record.adapter).trim(),
265
+ runLocality: safeString(record.runLocality).trim(),
266
+ lifecycleStatus: safeString(record.lifecycleStatus).trim(),
267
+ version: safeString(record.version).trim(),
268
+ sourceId: safeString(record.sourceId).trim(),
269
+ lifecycle,
270
+ payload: {
271
+ objectId: safeString(record.objectId).trim(),
272
+ name: safeString(record.name || record.sandboxName).trim(),
273
+ runtime: safeString(record.runtime).trim(),
274
+ adapter: safeString(record.adapter).trim(),
275
+ command: redactSecretsFromText(safeString(record.command)),
276
+ instructions: redactSecretsFromText(safeString(record.instructions)),
277
+ useDraft: Boolean(record.useDraft),
278
+ version: safeString(record.version).trim(),
279
+ schedulerRegistryId: safeString(record.schedulerRegistryId).trim(),
280
+ agentHost: safeString(record.agentHost).trim(),
281
+ timeoutMs: clampNumber(record.timeoutMs),
282
+ runInputs: safeInput,
283
+ inputSource: inputSummary ? inputSummary.source : "",
284
+ inputFieldCount: inputSummary ? inputSummary.fieldCount : 0,
285
+ inputFileCount: inputSummary ? inputSummary.fileCount : 0,
286
+ inputSummary
287
+ },
288
+ exports,
289
+ output: {
290
+ stdout: redactSecretsFromText(stdoutText),
291
+ stderr: redactSecretsFromText(stderrText),
292
+ error: redactSecretsFromText(errorText),
293
+ normalizedOutput: redactSecretsFromText(outputText),
294
+ exitCode: clampNumber(record.exitCode)
295
+ },
296
+ context: {
297
+ envRefsResolved: Array.isArray(record.envRefsResolved) ? record.envRefsResolved.slice() : [],
298
+ envRefsMissing: Array.isArray(record.envRefsMissing) ? record.envRefsMissing.slice() : [],
299
+ networkAllow: Boolean(record.networkAllow),
300
+ allowList: Array.isArray(record.allowList) ? record.allowList.slice() : [],
301
+ adapterMeta,
302
+ templateTrace
303
+ },
304
+ lineage,
305
+ swarm: record.swarm && typeof record.swarm === "object" ? record.swarm : null,
306
+ logTree: buildRunLogTree(record)
307
+ };
308
+ }
309
+
310
+ function buildRunTimeline(records) {
311
+ const list = Array.isArray(records) ? records : [];
312
+ const items = list
313
+ .map((record) => {
314
+ const normalized = normalizeRunConsoleRecord(record);
315
+ if (!normalized) return null;
316
+ const startedMs = parseDateMs(normalized.ranAt);
317
+ return {
318
+ runId: normalized.runId,
319
+ status: normalized.status,
320
+ durationMs: normalized.durationMs == null ? 0 : normalized.durationMs,
321
+ startedMs,
322
+ ranAt: normalized.ranAt
323
+ };
324
+ })
325
+ .filter(Boolean);
326
+
327
+ const max = items.reduce((m, it) => Math.max(m, it.durationMs || 0), 0);
328
+ return items.map((it) => ({
329
+ ...it,
330
+ barRatio: max > 0 ? Math.min(1, (it.durationMs || 0) / max) : 0
331
+ }));
332
+ }
333
+
334
+ function nodeMatchesQuery(node, query) {
335
+ const text = String(query || "").trim().toLowerCase();
336
+ if (!text) return true;
337
+ const haystack = [
338
+ node?.id,
339
+ node?.label,
340
+ node?.type,
341
+ node?.status,
342
+ node?.text
343
+ ]
344
+ .filter(Boolean)
345
+ .join(" ")
346
+ .toLowerCase();
347
+ return haystack.includes(text);
348
+ }
349
+
350
+ function nodeIsError(node) {
351
+ if (!node) return false;
352
+ if (node.type === "error") return true;
353
+ if (node.type === "stream" && node.id === "stderr") return true;
354
+ if (node.status === "failed" && node.type !== "stream") return true;
355
+ return false;
356
+ }
357
+
358
+ function filterNodeTree(nodes, predicate) {
359
+ if (!Array.isArray(nodes)) return [];
360
+ const out = [];
361
+ for (const node of nodes) {
362
+ const children = filterNodeTree(node?.children, predicate);
363
+ const selfMatches = predicate(node);
364
+ if (selfMatches || children.length > 0) {
365
+ out.push({ ...node, children });
366
+ }
367
+ }
368
+ return out;
369
+ }
370
+
371
+ function filterRunLogTree(tree, { query = "", errorsOnly = false } = {}) {
372
+ return filterNodeTree(tree, (node) => {
373
+ if (errorsOnly && !nodeIsError(node) && !(Array.isArray(node?.children) && node.children.some(nodeIsError))) {
374
+ return false;
375
+ }
376
+ return nodeMatchesQuery(node, query);
377
+ });
378
+ }
379
+
380
+ function formatRunDuration(ms) {
381
+ const n = clampNumber(ms);
382
+ if (n == null) return "—";
383
+ if (n < 1000) return `${Math.round(n)} ms`;
384
+ if (n < 60_000) return `${(n / 1000).toFixed(1)} s`;
385
+ const minutes = Math.floor(n / 60_000);
386
+ const seconds = Math.round((n % 60_000) / 1000);
387
+ return `${minutes}m ${seconds}s`;
388
+ }
389
+
390
+ function downloadRunBundle({ record, runId, sourceId } = {}) {
391
+ const normalized = normalizeRunConsoleRecord(record || {});
392
+ return {
393
+ kind: RUN_LOG_BUNDLE_KIND,
394
+ exportedAt: new Date().toISOString(),
395
+ runId: safeString(runId).trim() || (normalized ? normalized.runId : ""),
396
+ sourceId: safeString(sourceId).trim() || (normalized ? normalized.sourceId : ""),
397
+ record: normalized
398
+ };
399
+ }
400
+
401
+ export {
402
+ RUN_LOG_BUNDLE_KIND,
403
+ DEFAULT_EXPORT_TARGETS,
404
+ normalizeRunConsoleRecord,
405
+ deriveRunSummary,
406
+ deriveRunLifecycle,
407
+ buildRunLogTree,
408
+ buildRunTimeline,
409
+ filterRunLogTree,
410
+ formatRunDuration,
411
+ downloadRunBundle
412
+ };