@growthub/cli 0.9.3 → 0.9.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.
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/growthub.config.json +112 -0
- package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/package.json +1 -1
- package/assets/worker-kits/growthub-agency-portal-starter-v1/bundles/growthub-agency-portal-starter-v1.json +1 -0
- package/assets/worker-kits/growthub-agency-portal-starter-v1/kit.json +2 -0
- package/assets/worker-kits/growthub-creative-video-pipeline-v1/apps/creative-video-pipeline/package.json +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/SKILL.md +35 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/.env.example +41 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/README.md +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/settings/integrations/route.js +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +91 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +912 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/layout.jsx +14 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +105 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +680 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/growthub.config.json +53 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/jsconfig.json +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/auth/index.js +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/env.js +28 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/growthub-connection-normalizer.js +95 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/index.js +198 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/payments/index.js +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/persistence/index.js +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/persistence/postgres.js +16 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/persistence/provider-managed.js +16 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/persistence/qstash-kv.js +16 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/domain/integrations.js +185 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/domain/portal.js +150 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +232 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/next.config.js +10 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package-lock.json +976 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +17 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/postcss.config.mjs +3 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/vercel.json +5 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +13 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/adapter-contracts.md +86 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/vercel-serverless-deployment.md +46 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/growthub.config.json +49 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/check-self-improving-health.sh +60 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/promote-capability.mjs +37 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/propose-capability.mjs +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +45 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/deployment-handoff.md +61 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/supabase-setup-plan.md +26 -0
- package/dist/index.js +2903 -1596
- package/package.json +2 -1
package/assets/worker-kits/growthub-agency-portal-starter-v1/apps/agency-portal/growthub.config.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agency-portal-default",
|
|
3
|
+
"name": "Agency Portal Dashboard",
|
|
4
|
+
"description": "Default no-code composition shipped by growthub-agency-portal-starter-v1. It stitches the production portal capabilities catalog and Growthub MCP, BYO, and static integration lanes into a composable widget grid. Forks can edit ids, layouts, and bindings to tailor this composition to their governed workspace.",
|
|
5
|
+
"capabilities": [
|
|
6
|
+
"dashboard",
|
|
7
|
+
"clients",
|
|
8
|
+
"pipeline",
|
|
9
|
+
"content",
|
|
10
|
+
"tasks",
|
|
11
|
+
"finance",
|
|
12
|
+
"reports",
|
|
13
|
+
"metrics",
|
|
14
|
+
"client-results",
|
|
15
|
+
"operations",
|
|
16
|
+
"settings"
|
|
17
|
+
],
|
|
18
|
+
"pipelines": [],
|
|
19
|
+
"integrations": [
|
|
20
|
+
"windsor-ai",
|
|
21
|
+
"google-sheets-blended-data",
|
|
22
|
+
"google-analytics",
|
|
23
|
+
"shopify",
|
|
24
|
+
"meta-ads"
|
|
25
|
+
],
|
|
26
|
+
"canvas": {
|
|
27
|
+
"id": "agency-canvas",
|
|
28
|
+
"name": "Agency operating dashboard",
|
|
29
|
+
"scope": "workspace",
|
|
30
|
+
"layout": {
|
|
31
|
+
"columns": 12,
|
|
32
|
+
"rowHeight": 64,
|
|
33
|
+
"gap": 16,
|
|
34
|
+
"responsive": true
|
|
35
|
+
},
|
|
36
|
+
"widgets": [
|
|
37
|
+
{
|
|
38
|
+
"id": "mrr",
|
|
39
|
+
"kind": "chart-metric",
|
|
40
|
+
"title": "MRR",
|
|
41
|
+
"chart": "number",
|
|
42
|
+
"aggregate": "sum",
|
|
43
|
+
"slug": "agencyMetric",
|
|
44
|
+
"bindings": { "field": "mrr" },
|
|
45
|
+
"position": { "x": 0, "y": 0, "w": 3, "h": 2 }
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "pipeline-by-stage",
|
|
49
|
+
"kind": "chart-metric",
|
|
50
|
+
"title": "Pipeline by stage",
|
|
51
|
+
"chart": "bar",
|
|
52
|
+
"aggregate": "sum",
|
|
53
|
+
"slug": "opportunity",
|
|
54
|
+
"bindings": { "groupBy": "stage", "field": "value" },
|
|
55
|
+
"position": { "x": 3, "y": 0, "w": 6, "h": 4 }
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "client-health",
|
|
59
|
+
"kind": "fields",
|
|
60
|
+
"title": "Client health",
|
|
61
|
+
"slug": "client",
|
|
62
|
+
"bindings": { "fields": ["healthScore", "stage", "renewalDate"] },
|
|
63
|
+
"position": { "x": 9, "y": 0, "w": 3, "h": 2 }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "windsor-status",
|
|
67
|
+
"kind": "integration-card",
|
|
68
|
+
"title": "Windsor AI",
|
|
69
|
+
"slug": "windsor-ai",
|
|
70
|
+
"bindings": { "lane": "data-source", "adapter": "growthub-bridge" },
|
|
71
|
+
"position": { "x": 0, "y": 2, "w": 3, "h": 2 }
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "open-tasks",
|
|
75
|
+
"kind": "table",
|
|
76
|
+
"title": "Open tasks",
|
|
77
|
+
"slug": "task",
|
|
78
|
+
"bindings": { "filters": [{ "field": "done", "op": "eq", "value": false }] },
|
|
79
|
+
"position": { "x": 9, "y": 2, "w": 3, "h": 2 }
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "agent-chat",
|
|
83
|
+
"kind": "chat-session",
|
|
84
|
+
"title": "Agent chat",
|
|
85
|
+
"position": { "x": 0, "y": 4, "w": 4, "h": 4 }
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "active-workflow",
|
|
89
|
+
"kind": "workflow-runner",
|
|
90
|
+
"title": "Active pipeline",
|
|
91
|
+
"position": { "x": 4, "y": 4, "w": 4, "h": 4 }
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "recent-artifacts",
|
|
95
|
+
"kind": "artifact-viewer",
|
|
96
|
+
"title": "Recent reports",
|
|
97
|
+
"mediaPreview": true,
|
|
98
|
+
"position": { "x": 8, "y": 4, "w": 4, "h": 4 }
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"bindings": {
|
|
102
|
+
"chatToCanvas": true,
|
|
103
|
+
"workflowOutputsToArtifacts": true,
|
|
104
|
+
"sessionContext": true,
|
|
105
|
+
"portalCapabilities": true
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"provenance": {
|
|
109
|
+
"createdBy": "cli",
|
|
110
|
+
"note": "Shipped with growthub-agency-portal-starter-v1; safe to edit inside a governed fork and validate through the starter export smoke path."
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"apps/agency-portal/package.json",
|
|
55
55
|
"apps/agency-portal/package-lock.json",
|
|
56
56
|
"apps/agency-portal/next.config.js",
|
|
57
|
+
"apps/agency-portal/growthub.config.json",
|
|
57
58
|
"apps/agency-portal/app/settings/integrations/page.jsx",
|
|
58
59
|
"apps/agency-portal/lib/adapters/env.js",
|
|
59
60
|
"apps/agency-portal/lib/adapters/integrations/index.js",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"apps/agency-portal/next.config.js",
|
|
72
72
|
"apps/agency-portal/postcss.config.mjs",
|
|
73
73
|
"apps/agency-portal/vercel.json",
|
|
74
|
+
"apps/agency-portal/growthub.config.json",
|
|
74
75
|
"apps/agency-portal/app/layout.jsx",
|
|
75
76
|
"apps/agency-portal/app/page.jsx",
|
|
76
77
|
"apps/agency-portal/app/globals.css",
|
|
@@ -118,6 +119,7 @@
|
|
|
118
119
|
"apps/agency-portal",
|
|
119
120
|
"apps/agency-portal/package-lock.json",
|
|
120
121
|
"apps/agency-portal/next.config.js",
|
|
122
|
+
"apps/agency-portal/growthub.config.json",
|
|
121
123
|
"apps/agency-portal/lib/adapters",
|
|
122
124
|
"templates",
|
|
123
125
|
"templates/project.md",
|
|
@@ -19,7 +19,16 @@ selfEval:
|
|
|
19
19
|
- Operator contract read before any material change.
|
|
20
20
|
maxRetries: 3
|
|
21
21
|
traceTo: .growthub-fork/trace.jsonl
|
|
22
|
-
helpers:
|
|
22
|
+
helpers:
|
|
23
|
+
- path: helpers/propose-capability.mjs
|
|
24
|
+
verb: propose-capability
|
|
25
|
+
description: Propose a reusable capability from a pipeline run (self-improving feature).
|
|
26
|
+
- path: helpers/promote-capability.mjs
|
|
27
|
+
verb: promote-capability
|
|
28
|
+
description: Promote a capability proposal to the active library.
|
|
29
|
+
- path: helpers/check-self-improving-health.sh
|
|
30
|
+
verb: check-self-improving-health
|
|
31
|
+
description: Validate self-improving workspace primitives and proposal dirs.
|
|
23
32
|
subSkills: []
|
|
24
33
|
mcpTools: []
|
|
25
34
|
---
|
|
@@ -91,6 +100,31 @@ Helpers live under `helpers/`. When an existing shell snippet in `skills.md` is
|
|
|
91
100
|
|
|
92
101
|
(None declared at the baseline; see `helpers/README.md` for the pattern.)
|
|
93
102
|
|
|
103
|
+
## Self-improving feature (optional extension)
|
|
104
|
+
|
|
105
|
+
Any governed workspace can activate the self-improving loop — no separate kit required.
|
|
106
|
+
|
|
107
|
+
After a successful pipeline or orchestrator run, propose a reusable capability:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Propose a capability from a run (anchors to trace.jsonl event)
|
|
111
|
+
node helpers/propose-capability.mjs --from-run <run-id> --summary "what this run produced"
|
|
112
|
+
# or directly:
|
|
113
|
+
growthub workspace improve propose --from-run <run-id>
|
|
114
|
+
|
|
115
|
+
# Review proposals
|
|
116
|
+
growthub workspace improve list
|
|
117
|
+
growthub workspace improve inspect <slug>
|
|
118
|
+
growthub workspace improve promote <slug>
|
|
119
|
+
|
|
120
|
+
# Health check (includes self-improving feature checks)
|
|
121
|
+
bash helpers/check-self-improving-health.sh
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Proposals are governed writes to `.growthub-fork/capabilities/proposals/`. Every proposal references the originating `trace.jsonl` event by trace-event ID — no duplicated schema. Each lifecycle transition (`proposed → promoted | rejected`) appends a typed trace event (`capability_proposed`, `capability_promoted`, `capability_rejected`).
|
|
125
|
+
|
|
126
|
+
This is an **optional feature extension** on the base governed workspace primitive, not a separate workspace type.
|
|
127
|
+
|
|
94
128
|
## MCP routing (optional)
|
|
95
129
|
|
|
96
130
|
If a concrete MCP server is available for this fork, list its tool IDs in `mcpTools[]`. Growthub's baseline ships declarative-only routing vocabulary — the CLI does not run an MCP server at v1. This field lets future MCP integration light up additively without a breaking change.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Core adapter selectors
|
|
2
|
+
AGENCY_PORTAL_DEPLOY_TARGET=vercel
|
|
3
|
+
AGENCY_PORTAL_DATA_ADAPTER=provider-managed
|
|
4
|
+
AGENCY_PORTAL_AUTH_ADAPTER=provider-managed
|
|
5
|
+
AGENCY_PORTAL_PAYMENT_ADAPTER=none
|
|
6
|
+
|
|
7
|
+
# Integration mode
|
|
8
|
+
# - growthub-bridge: hosted Growthub account authority
|
|
9
|
+
# - byo-api-key: workspace-owned connection metadata
|
|
10
|
+
# - static: local starter catalog only
|
|
11
|
+
AGENCY_PORTAL_INTEGRATION_ADAPTER=growthub-bridge
|
|
12
|
+
|
|
13
|
+
# Hosted bridge authority
|
|
14
|
+
GROWTHUB_BRIDGE_BASE_URL=https://www.growthub.ai
|
|
15
|
+
GROWTHUB_BRIDGE_INTEGRATIONS_PATH=/api/mcp/accounts
|
|
16
|
+
GROWTHUB_BRIDGE_ACCESS_TOKEN=
|
|
17
|
+
GROWTHUB_BRIDGE_USER_ID=
|
|
18
|
+
|
|
19
|
+
# Optional Windsor reporting lane.
|
|
20
|
+
# Hybrid first boot: keep growthub-bridge authority and set WINDSOR_API_KEY
|
|
21
|
+
# to mark Windsor AI + Google Sheets blended data connected locally.
|
|
22
|
+
AGENCY_PORTAL_REPORTING_ADAPTER=windsor
|
|
23
|
+
WINDSOR_API_KEY=
|
|
24
|
+
|
|
25
|
+
# Optional BYO connection metadata
|
|
26
|
+
AGENCY_PORTAL_BYO_CONNECTIONS_JSON=
|
|
27
|
+
|
|
28
|
+
# Optional payment/auth/database env selected by adapter values above
|
|
29
|
+
DATABASE_URL=
|
|
30
|
+
QSTASH_KV_REST_URL=
|
|
31
|
+
QSTASH_KV_REST_TOKEN=
|
|
32
|
+
AUTH_SECRET=
|
|
33
|
+
AUTH_ISSUER=
|
|
34
|
+
AUTH_CLIENT_ID=
|
|
35
|
+
AUTH_CLIENT_SECRET=
|
|
36
|
+
PAYMENT_SECRET_KEY=
|
|
37
|
+
PAYMENT_WEBHOOK_SECRET=
|
|
38
|
+
|
|
39
|
+
# Optional app settings
|
|
40
|
+
CRON_SECRET=
|
|
41
|
+
PORTAL_USER_ID=
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Growthub Workspace App
|
|
2
|
+
|
|
3
|
+
This app is the Vercel/serverless runtime payload for `growthub-workspace-starter-v1`.
|
|
4
|
+
|
|
5
|
+
It intentionally depends on adapter contracts:
|
|
6
|
+
|
|
7
|
+
- `AGENCY_PORTAL_DATA_ADAPTER`
|
|
8
|
+
- `AGENCY_PORTAL_AUTH_ADAPTER`
|
|
9
|
+
- `AGENCY_PORTAL_PAYMENT_ADAPTER`
|
|
10
|
+
- `AGENCY_PORTAL_INTEGRATION_ADAPTER`
|
|
11
|
+
- `GROWTHUB_BRIDGE_BASE_URL`
|
|
12
|
+
- `GROWTHUB_BRIDGE_INTEGRATIONS_PATH`
|
|
13
|
+
- `GROWTHUB_BRIDGE_ACCESS_TOKEN`
|
|
14
|
+
- `GROWTHUB_BRIDGE_USER_ID`
|
|
15
|
+
- `AGENCY_PORTAL_BYO_CONNECTIONS_JSON`
|
|
16
|
+
|
|
17
|
+
The Growthub local-first operator shell remains at `../../studio`.
|
|
18
|
+
|
|
19
|
+
Settings exposes two integration lanes:
|
|
20
|
+
|
|
21
|
+
- Data sources: Windsor AI, Google Sheets blended data, Google Analytics, Shopify, Meta Facebook/Instagram.
|
|
22
|
+
- Workspace integrations: Asana, Slack, GoHighLevel, Google Drive, Notion.
|
|
23
|
+
|
|
24
|
+
Use `AGENCY_PORTAL_INTEGRATION_ADAPTER=growthub-bridge` when the deployed app should read connection state from the Growthub GH app MCP bridge. The reusable primitive is `lib/adapters/integrations/growthub-connection-normalizer.js`; it accepts SDK/profile-style `integrations[]` payloads and GH app MCP `accounts[]` payloads, then emits the same normalized object shape used by `byo-api-key`. Keep provider tokens in the hosted authority layer or named env vars; this app consumes normalized connection metadata only.
|
|
25
|
+
|
|
26
|
+
For first boot, the bundled app also supports a hybrid path: keep `AGENCY_PORTAL_INTEGRATION_ADAPTER=growthub-bridge` and set `WINDSOR_API_KEY` locally. That overlays connected state for Windsor AI and Google Sheets blended data without moving the rest of the portal off the hosted bridge authority path.
|
|
27
|
+
|
|
28
|
+
## Run
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install
|
|
32
|
+
npm run dev
|
|
33
|
+
npm run build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Deploy
|
|
37
|
+
|
|
38
|
+
Use this directory as the Vercel project root.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { describeIntegrationAdapter, listAgencyPortalIntegrations } from "@/lib/adapters/integrations";
|
|
3
|
+
import { groupIntegrationsByLane } from "@/lib/domain/integrations";
|
|
4
|
+
async function GET() {
|
|
5
|
+
const integrations = await listAgencyPortalIntegrations();
|
|
6
|
+
return NextResponse.json({
|
|
7
|
+
adapter: describeIntegrationAdapter(),
|
|
8
|
+
...groupIntegrationsByLane(integrations)
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
GET
|
|
13
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { describeAuthAdapter } from "@/lib/adapters/auth";
|
|
3
|
+
import { readAdapterConfig } from "@/lib/adapters/env";
|
|
4
|
+
import { describeIntegrationAdapter, listAgencyPortalIntegrations } from "@/lib/adapters/integrations";
|
|
5
|
+
import { describePaymentAdapter } from "@/lib/adapters/payments";
|
|
6
|
+
import { describePersistenceAdapter } from "@/lib/adapters/persistence";
|
|
7
|
+
import { groupIntegrationsByLane } from "@/lib/domain/integrations";
|
|
8
|
+
import { buildPortalWorkspace, portalCapabilities } from "@/lib/domain/portal";
|
|
9
|
+
import {
|
|
10
|
+
describePersistenceMode,
|
|
11
|
+
readWorkspaceConfig,
|
|
12
|
+
writeWorkspaceConfig
|
|
13
|
+
} from "@/lib/workspace-config";
|
|
14
|
+
|
|
15
|
+
const ALLOWED_PATCH_FIELDS = new Set(["dashboards", "widgetTypes", "canvas"]);
|
|
16
|
+
|
|
17
|
+
async function GET() {
|
|
18
|
+
const integrations = await listAgencyPortalIntegrations();
|
|
19
|
+
const config = readAdapterConfig();
|
|
20
|
+
const adapters = {
|
|
21
|
+
persistence: describePersistenceAdapter(),
|
|
22
|
+
auth: describeAuthAdapter(),
|
|
23
|
+
payments: describePaymentAdapter(),
|
|
24
|
+
integrations: describeIntegrationAdapter()
|
|
25
|
+
};
|
|
26
|
+
const settings = {
|
|
27
|
+
integrations: groupIntegrationsByLane(integrations)
|
|
28
|
+
};
|
|
29
|
+
const workspaceConfig = await readWorkspaceConfig();
|
|
30
|
+
const persistence = describePersistenceMode();
|
|
31
|
+
return NextResponse.json({
|
|
32
|
+
config,
|
|
33
|
+
adapters,
|
|
34
|
+
capabilities: portalCapabilities,
|
|
35
|
+
settings,
|
|
36
|
+
workspace: buildPortalWorkspace({ config, adapters, integrations: settings.integrations }),
|
|
37
|
+
workspaceConfig,
|
|
38
|
+
workspaceConfigPersistence: persistence
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function PATCH(request) {
|
|
43
|
+
let patch;
|
|
44
|
+
try {
|
|
45
|
+
patch = await request.json();
|
|
46
|
+
} catch {
|
|
47
|
+
return NextResponse.json({ error: "invalid json body" }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
50
|
+
return NextResponse.json({ error: "patch must be a plain object" }, { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
const unknown = Object.keys(patch).filter((key) => !ALLOWED_PATCH_FIELDS.has(key));
|
|
53
|
+
if (unknown.length) {
|
|
54
|
+
return NextResponse.json(
|
|
55
|
+
{ error: "patch contains unknown fields", details: unknown, allowed: Array.from(ALLOWED_PATCH_FIELDS) },
|
|
56
|
+
{ status: 400 }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const sanitized = {};
|
|
60
|
+
for (const key of ALLOWED_PATCH_FIELDS) {
|
|
61
|
+
if (Object.prototype.hasOwnProperty.call(patch, key)) {
|
|
62
|
+
sanitized[key] = patch[key];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const next = await writeWorkspaceConfig(sanitized);
|
|
67
|
+
return NextResponse.json({ workspaceConfig: next });
|
|
68
|
+
} catch (error) {
|
|
69
|
+
if (error.code === "WORKSPACE_PERSISTENCE_READ_ONLY") {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{
|
|
72
|
+
error: "workspace config is read-only in this runtime",
|
|
73
|
+
reason: error.message,
|
|
74
|
+
adapter: error.adapter,
|
|
75
|
+
guidance:
|
|
76
|
+
"Edit growthub.config.json locally, or set WORKSPACE_CONFIG_ALLOW_FS_WRITE=true on a writable runtime."
|
|
77
|
+
},
|
|
78
|
+
{ status: 409 }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (error.code === "WORKSPACE_PERSISTENCE_PATH_REFUSED") {
|
|
82
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
83
|
+
}
|
|
84
|
+
if (error.code === "INVALID_WORKSPACE_CONFIG") {
|
|
85
|
+
return NextResponse.json({ error: error.message, details: error.details }, { status: 400 });
|
|
86
|
+
}
|
|
87
|
+
return NextResponse.json({ error: error.message || "failed to write workspace config" }, { status: 500 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { GET, PATCH };
|