@growthub/cli 0.13.5 → 0.13.6

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 (35) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/QUICKSTART.md +19 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/.env.example +8 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/README.md +4 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/action/execute/route.js +60 -0
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/actions/route.js +50 -0
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connect-session/route.js +68 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/connection-status/route.js +56 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/proxy/route.js +67 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/integrations/nango/status/route.js +50 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +161 -50
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/NangoConnectionPanel.jsx +496 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +104 -17
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/nango/page.jsx +167 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +1 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +18 -7
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +17 -9
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +16 -14
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/env.js +7 -0
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/index.js +38 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-adapter.js +552 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-config-loader.js +202 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/nango/nango-schema.js +303 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +49 -10
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +1 -1
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/nango.js +49 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +4 -2
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +2 -2
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +2 -1
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +102 -3
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +1 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +2 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/project-management.config.json +276 -0
  34. package/dist/index.js +127 -44
  35. package/package.json +1 -1
@@ -10,6 +10,25 @@ This kit is the canonical v1 starter primitive. Run `growthub starter init --nam
10
10
 
11
11
  After that, every customization you make stays yours — the Self-Healing Fork Sync Agent will propose upstream additions non-destructively, honouring your policy.
12
12
 
13
+ ## Official workspace paths
14
+
15
+ Workspace 1 stays the blank governed workspace:
16
+
17
+ ```bash
18
+ growthub starter init --name "My Workspace" --out ./my-workspace
19
+ ```
20
+
21
+ Workspace 2 is the opinionated project-management template path. It builds on the same app and adapter surface, but seeds API Registry, Data Source, Sandbox Environment workflow, and dashboard placeholders for project task deltas. It stores no provider secrets, OAuth connection ids, or provider task data.
22
+
23
+ ```bash
24
+ growthub starter init \
25
+ --name "Project Management Workspace" \
26
+ --out ./project-management-workspace \
27
+ --seed-config project-management
28
+ ```
29
+
30
+ After export, set `NANGO_SECRET_KEY` in the workspace runtime, complete provider OAuth, then fill `providerConfigKey`, `connectionIds`, and project identifiers in the Data Model before running the workflow.
31
+
13
32
  ## Run the bundled Vite UI shell
14
33
 
15
34
  ```bash
@@ -10,6 +10,14 @@ GROWTHUB_WORKSPACE_PAYMENT_ADAPTER=none
10
10
  # - static: local starter catalog only
11
11
  GROWTHUB_WORKSPACE_INTEGRATION_ADAPTER=growthub-bridge
12
12
 
13
+ # Nango thin adapter — row-scoped, not a workspace-wide adapter.
14
+ # Set NANGO_SECRET_KEY when any api-registry row has connectorKind: "nango".
15
+ # Cloud mode reaches Nango Cloud; self-hosted requires NANGO_HOST_URL.
16
+ NANGO_MODE=cloud
17
+ NANGO_HOST_URL=
18
+ NANGO_ENVIRONMENT=dev
19
+ NANGO_SECRET_KEY=
20
+
13
21
  # Hosted bridge authority
14
22
  GROWTHUB_BRIDGE_BASE_URL=https://www.growthub.ai
15
23
  GROWTHUB_BRIDGE_INTEGRATIONS_PATH=/api/mcp/accounts
@@ -13,6 +13,10 @@ It intentionally depends on adapter contracts:
13
13
  - `GROWTHUB_BRIDGE_ACCESS_TOKEN`
14
14
  - `GROWTHUB_BRIDGE_USER_ID`
15
15
  - `GROWTHUB_WORKSPACE_BYO_CONNECTIONS_JSON`
16
+ - `NANGO_SECRET_KEY` (required when any api-registry row uses `connectorKind: "nango"`)
17
+ - `NANGO_HOST_URL` (self-hosted Nango only)
18
+ - `NANGO_ENVIRONMENT` (default `dev`)
19
+ - `NANGO_MODE` (`cloud` | `self-hosted`, default `cloud`)
16
20
 
17
21
  The Growthub local-first operator shell remains at `../../studio`.
18
22
 
@@ -0,0 +1,60 @@
1
+ /**
2
+ * POST /api/workspace/integrations/nango/action/execute
3
+ *
4
+ * Executes a declared Nango action function against the connection
5
+ * identified by `providerConfigKey` + `connectionId`. Used by workspace
6
+ * agents as a tool-call surface.
7
+ *
8
+ * Request body:
9
+ * {
10
+ * providerConfigKey: string,
11
+ * connectionId: string,
12
+ * action: string, // action function name from nango.yaml
13
+ * input?: object | array // arguments to the action
14
+ * }
15
+ *
16
+ * Response — success: 200 { ok: true, action, providerConfigKey, connectionId, result, environment }
17
+ * Response — validation failure: 400 { ok: false, error, details }
18
+ * Response — Nango not configured: 503 { ok: false, error, code }
19
+ * Response — upstream failure: 502 { ok: false, error }
20
+ */
21
+
22
+ import { NextResponse } from "next/server";
23
+ import { executeAction, validateActionExecuteRequest } from "@/lib/adapters/integrations/nango";
24
+
25
+ async function POST(request) {
26
+ let body;
27
+ try {
28
+ body = await request.json();
29
+ } catch {
30
+ return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
31
+ }
32
+
33
+ let validated;
34
+ try {
35
+ validated = validateActionExecuteRequest(body);
36
+ } catch (error) {
37
+ return NextResponse.json(
38
+ { ok: false, error: error.message, details: error.details || null },
39
+ { status: 400 }
40
+ );
41
+ }
42
+
43
+ try {
44
+ const result = await executeAction(validated);
45
+ return NextResponse.json({ ok: true, ...result });
46
+ } catch (error) {
47
+ if (error.code === "NANGO_NOT_CONFIGURED" || error.code === "NANGO_SDK_UNAVAILABLE" || error.code === "NANGO_SDK_SHAPE") {
48
+ return NextResponse.json(
49
+ { ok: false, error: error.message, code: error.code },
50
+ { status: 503 }
51
+ );
52
+ }
53
+ return NextResponse.json(
54
+ { ok: false, error: error?.message || "nango action execution failed" },
55
+ { status: 502 }
56
+ );
57
+ }
58
+ }
59
+
60
+ export { POST };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * GET /api/workspace/integrations/nango/actions
3
+ *
4
+ * Lists the actions declared in `nango.yaml` for a given provider config key
5
+ * (or, when no key is provided, returns the full discoverable set). The
6
+ * response shape is MCP-compatible: each action exposes a name and (when
7
+ * provided by Nango) an input/output schema.
8
+ *
9
+ * Query parameters (optional):
10
+ * - providerConfigKey filter to actions for a single provider
11
+ *
12
+ * Response shape:
13
+ * { ok: true, providerConfigKey, probedShape, actions: Array, hint: string|null }
14
+ */
15
+
16
+ import { NextResponse } from "next/server";
17
+ import { listActions, validateActionsListInput } from "@/lib/adapters/integrations/nango";
18
+
19
+ async function GET(request) {
20
+ const url = new URL(request.url);
21
+ const raw = {
22
+ providerConfigKey: url.searchParams.get("providerConfigKey") || undefined
23
+ };
24
+ let input;
25
+ try {
26
+ input = validateActionsListInput(raw);
27
+ } catch (error) {
28
+ return NextResponse.json(
29
+ { ok: false, error: error.message, details: error.details || null },
30
+ { status: 400 }
31
+ );
32
+ }
33
+ try {
34
+ const result = await listActions(input);
35
+ return NextResponse.json({ ok: true, ...result });
36
+ } catch (error) {
37
+ if (error.code === "NANGO_NOT_CONFIGURED" || error.code === "NANGO_SDK_UNAVAILABLE") {
38
+ return NextResponse.json(
39
+ { ok: false, error: error.message, code: error.code },
40
+ { status: 503 }
41
+ );
42
+ }
43
+ return NextResponse.json(
44
+ { ok: false, error: error?.message || "nango actions list failed" },
45
+ { status: 502 }
46
+ );
47
+ }
48
+ }
49
+
50
+ export { GET };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * POST /api/workspace/integrations/nango/connect-session
3
+ *
4
+ * Creates a Nango Connect Session for the given providerConfigKey and
5
+ * returns a short-lived connect_link the browser opens to start OAuth.
6
+ * The Nango Connect UI mints tokens directly against Nango — the workspace
7
+ * never sees the resulting OAuth credentials. The token returned here is a
8
+ * Connect Session token (handoff only), not a provider credential.
9
+ *
10
+ * Lifecycle: Nango creates the connectionId AFTER the user completes OAuth
11
+ * and delivers it through the auth webhook. A normal Connect Session
12
+ * therefore does NOT need a connectionId as input — only the explicit
13
+ * Reconnect path (`reconnect: true`) operates on an existing connectionId.
14
+ *
15
+ * Request body:
16
+ * {
17
+ * providerConfigKey: string, // required, alphanumeric (+ _.-) <= 64 chars
18
+ * reconnect?: boolean, // true → use the SDK's reconnect-session path
19
+ * connectionId?: string, // REQUIRED when reconnect=true; ignored for a normal Create Connect Session
20
+ * endUser?: { id?: string, email?: string },
21
+ * tags?: Record<string, string> // echoed back in the Nango auth webhook so the workspace can map the eventual connectionId to the originating row
22
+ * }
23
+ *
24
+ * Response — success: 200 { ok: true, providerConfigKey, environment, mode, token, connectLink, sdkMethod, tagsEchoed }
25
+ * Response — validation failure: 400 { ok: false, error, details }
26
+ * Response — Nango not configured / SDK missing: 503 { ok: false, error, code }
27
+ * Response — upstream failure: 502 { ok: false, error }
28
+ */
29
+
30
+ import { NextResponse } from "next/server";
31
+ import { createConnectSession, validateConnectSessionRequest } from "@/lib/adapters/integrations/nango";
32
+
33
+ async function POST(request) {
34
+ let body;
35
+ try {
36
+ body = await request.json();
37
+ } catch {
38
+ return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
39
+ }
40
+
41
+ let validated;
42
+ try {
43
+ validated = validateConnectSessionRequest(body);
44
+ } catch (error) {
45
+ return NextResponse.json(
46
+ { ok: false, error: error.message, details: error.details || null },
47
+ { status: 400 }
48
+ );
49
+ }
50
+
51
+ try {
52
+ const result = await createConnectSession(validated);
53
+ return NextResponse.json({ ok: true, ...result });
54
+ } catch (error) {
55
+ if (error.code === "NANGO_NOT_CONFIGURED" || error.code === "NANGO_SDK_UNAVAILABLE" || error.code === "NANGO_SDK_SHAPE") {
56
+ return NextResponse.json(
57
+ { ok: false, error: error.message, code: error.code },
58
+ { status: 503 }
59
+ );
60
+ }
61
+ return NextResponse.json(
62
+ { ok: false, error: error?.message || "nango connect session failed" },
63
+ { status: 502 }
64
+ );
65
+ }
66
+ }
67
+
68
+ export { POST };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * POST /api/workspace/integrations/nango/connection-status
3
+ *
4
+ * Returns the safe (non-credential) status summary for a specific Nango
5
+ * connection. Used by the NangoConnectionPanel sidecar to verify a
6
+ * per-row connection without exposing OAuth tokens to the browser.
7
+ *
8
+ * Request body:
9
+ * { providerConfigKey: string, connectionId: string }
10
+ *
11
+ * Response — connected: 200 { ok: true, status: "connected", providerConfigKey, connectionId, environment, connection: { ...safe fields... } }
12
+ * Response — not yet connected: 200 { ok: true, status: "not-connected", providerConfigKey, connectionId, environment, reason }
13
+ * Response — validation failure: 400 { ok: false, error, details }
14
+ * Response — Nango not configured: 503 { ok: false, error, code }
15
+ * Response — upstream failure: 502 { ok: false, error }
16
+ */
17
+
18
+ import { NextResponse } from "next/server";
19
+ import { getConnectionSummary, validateConnectionSummaryRequest } from "@/lib/adapters/integrations/nango";
20
+
21
+ async function POST(request) {
22
+ let body;
23
+ try {
24
+ body = await request.json();
25
+ } catch {
26
+ return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
27
+ }
28
+
29
+ let validated;
30
+ try {
31
+ validated = validateConnectionSummaryRequest(body);
32
+ } catch (error) {
33
+ return NextResponse.json(
34
+ { ok: false, error: error.message, details: error.details || null },
35
+ { status: 400 }
36
+ );
37
+ }
38
+
39
+ try {
40
+ const result = await getConnectionSummary(validated);
41
+ return NextResponse.json({ ok: true, ...result });
42
+ } catch (error) {
43
+ if (error.code === "NANGO_NOT_CONFIGURED" || error.code === "NANGO_SDK_UNAVAILABLE" || error.code === "NANGO_SDK_SHAPE") {
44
+ return NextResponse.json(
45
+ { ok: false, error: error.message, code: error.code },
46
+ { status: 503 }
47
+ );
48
+ }
49
+ return NextResponse.json(
50
+ { ok: false, error: error?.message || "nango connection status failed" },
51
+ { status: 502 }
52
+ );
53
+ }
54
+ }
55
+
56
+ export { POST };
@@ -0,0 +1,67 @@
1
+ /**
2
+ * POST /api/workspace/integrations/nango/proxy
3
+ *
4
+ * Proxies an API request through Nango using the connection identified by
5
+ * `providerConfigKey` + `connectionId`. The Nango SDK injects credentials
6
+ * server-side; the browser never holds the upstream provider's auth.
7
+ *
8
+ * Request body:
9
+ * {
10
+ * providerConfigKey: string, // Nango integration config key
11
+ * connectionId: string, // Nango connection id
12
+ * method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
13
+ * endpoint: string, // path or absolute URL
14
+ * headers?: object, // forbidden: Authorization, X-API-Key, etc.
15
+ * params?: object,
16
+ * data?: any,
17
+ * retries?: number, // 0..10
18
+ * timeoutMs?: number // 0..60000
19
+ * }
20
+ *
21
+ * Response — success:
22
+ * { ok: true, status: number, data: any, environment: string }
23
+ *
24
+ * Response — validation failure: 400 { ok: false, error, details }
25
+ * Response — Nango not configured: 503 { ok: false, error, code }
26
+ * Response — upstream failure: 502 { ok: false, error }
27
+ */
28
+
29
+ import { NextResponse } from "next/server";
30
+ import { proxyRequest, validateProxyRequest } from "@/lib/adapters/integrations/nango";
31
+
32
+ async function POST(request) {
33
+ let body;
34
+ try {
35
+ body = await request.json();
36
+ } catch {
37
+ return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
38
+ }
39
+
40
+ let validated;
41
+ try {
42
+ validated = validateProxyRequest(body);
43
+ } catch (error) {
44
+ return NextResponse.json(
45
+ { ok: false, error: error.message, details: error.details || null },
46
+ { status: 400 }
47
+ );
48
+ }
49
+
50
+ try {
51
+ const result = await proxyRequest(validated);
52
+ return NextResponse.json({ ok: true, ...result });
53
+ } catch (error) {
54
+ if (error.code === "NANGO_NOT_CONFIGURED" || error.code === "NANGO_SDK_UNAVAILABLE" || error.code === "NANGO_SDK_SHAPE") {
55
+ return NextResponse.json(
56
+ { ok: false, error: error.message, code: error.code },
57
+ { status: 503 }
58
+ );
59
+ }
60
+ return NextResponse.json(
61
+ { ok: false, error: error?.message || "nango proxy failed" },
62
+ { status: 502 }
63
+ );
64
+ }
65
+ }
66
+
67
+ export { POST };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * GET /api/workspace/integrations/nango/status
3
+ *
4
+ * Returns Nango connection health for the no-code Integrations panel.
5
+ *
6
+ * Query parameters (all optional):
7
+ * - providerConfigKey probe a specific provider connection
8
+ * - connectionId probe a specific connection (paired with providerConfigKey)
9
+ * - mode override mode for the probe ("cloud" | "self-hosted")
10
+ * - hostUrl override host URL for self-hosted probes
11
+ *
12
+ * Response shape:
13
+ * { status: "connected" | "disconnected" | "unconfigured", ... }
14
+ *
15
+ * Authority contract: never returns the Nango secret key. Never echoes
16
+ * credential-shaped fields from upstream SDK responses.
17
+ */
18
+
19
+ import { NextResponse } from "next/server";
20
+ import { getStatus, validateConnectionStatusRequest } from "@/lib/adapters/integrations/nango";
21
+
22
+ async function GET(request) {
23
+ const url = new URL(request.url);
24
+ const raw = {
25
+ providerConfigKey: url.searchParams.get("providerConfigKey") || undefined,
26
+ connectionId: url.searchParams.get("connectionId") || undefined,
27
+ mode: url.searchParams.get("mode") || undefined,
28
+ hostUrl: url.searchParams.get("hostUrl") || undefined
29
+ };
30
+ let input;
31
+ try {
32
+ input = validateConnectionStatusRequest(raw);
33
+ } catch (error) {
34
+ return NextResponse.json(
35
+ { error: error.message, details: error.details || null },
36
+ { status: 400 }
37
+ );
38
+ }
39
+ try {
40
+ const result = await getStatus(input);
41
+ return NextResponse.json(result);
42
+ } catch (error) {
43
+ return NextResponse.json(
44
+ { error: error?.message || "nango status probe failed" },
45
+ { status: 500 }
46
+ );
47
+ }
48
+ }
49
+
50
+ export { GET };