@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
@@ -305,7 +305,7 @@ function findSandboxRowsForRegistry(workspaceConfig, integrationId) {
305
305
  if (!sandboxObject) return [];
306
306
  const rows = Array.isArray(sandboxObject.rows) ? sandboxObject.rows : [];
307
307
  return rows.filter((row) => {
308
- const graph = parseOrchestrationGraph(row?.orchestrationGraph);
308
+ const graph = parseOrchestrationGraph(row?.orchestrationConfig || row?.orchestrationGraph);
309
309
  if (!graph?.nodes) return String(row?.schedulerRegistryId || "").trim() === id;
310
310
  return graph.nodes.some(
311
311
  (node) => node?.type === "api-registry-call"
@@ -359,7 +359,7 @@ function buildSandboxRowFromApiRegistry(workspaceConfig, registryRow, options =
359
359
  lastRunId: "",
360
360
  lastSourceId: "",
361
361
  lastResponse: "",
362
- orchestrationGraph: serializeOrchestrationGraph(graph),
362
+ orchestrationConfig: serializeOrchestrationGraph(graph),
363
363
  description: String(options.description || registryRow?.description || "").trim()
364
364
  };
365
365
  }
@@ -417,6 +417,7 @@ function deriveManualObjectTable(object, options = {}) {
417
417
  source,
418
418
  objectType: object.objectType || "custom",
419
419
  icon: object.icon || null,
420
+ locked: object.locked !== undefined ? Boolean(object.locked) : Boolean(object.objectType && object.objectType !== "custom"),
420
421
  pickerHidden: Boolean(object.pickerHidden),
421
422
  columns: hydratedColumns,
422
423
  rows: hydratedRows,
@@ -888,7 +889,7 @@ const OBJECT_TYPE_PRESETS = {
888
889
  "lastRunId",
889
890
  "lastSourceId",
890
891
  "lastResponse",
891
- "orchestrationGraph",
892
+ "orchestrationConfig",
892
893
  "description",
893
894
  "resolverTemplateId",
894
895
  "connectorKind",
@@ -96,6 +96,15 @@ const KNOWN_SANDBOX_AGENT_HOSTS = [
96
96
  ];
97
97
 
98
98
  const NORMALIZED_OBJECT_FIELD_IDS = ["id", "label", "secondaryLabel", "entityType", "provider", "lane", "status"];
99
+
100
+ // API Registry V1 — Nango binding fields are additive and OPTIONAL on the
101
+ // existing `objectType: "api-registry"` row shape (whose canonical columns
102
+ // are owned by `lib/workspace-data-model.js`). They only carry meaning when
103
+ // the row's `connectorKind === "nango"`. The HTTP, MCP, Chrome, and tool
104
+ // connectorKinds keep working unchanged.
105
+ const KNOWN_NANGO_MODES = ["cloud", "self-hosted"];
106
+ const NANGO_PROVIDER_CONFIG_KEY_MAX = 64;
107
+ const NANGO_PROVIDER_CONFIG_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
99
108
  const WORKSPACE_TEMPLATE_KIND = "growthub-workspace-template";
100
109
  const WORKSPACE_TEMPLATE_VERSION = 1;
101
110
  const WORKSPACE_TEMPLATE_SOURCE = "growthub-custom-workspace-starter-v1";
@@ -1016,9 +1025,11 @@ function validateSandboxEnvironmentRow(row, path, errors) {
1016
1025
  errors.push(`${path}.intelligenceAdapterMode must be one of ${INTELLIGENCE_ADAPTER_MODES.join(", ")}`);
1017
1026
  }
1018
1027
  }
1019
- if (row.orchestrationGraph !== undefined && row.orchestrationGraph !== null && row.orchestrationGraph !== "") {
1020
- if (typeof row.orchestrationGraph !== "string" && (typeof row.orchestrationGraph !== "object" || Array.isArray(row.orchestrationGraph))) {
1021
- errors.push(`${path}.orchestrationGraph must be a JSON string or plain object`);
1028
+ for (const field of ["orchestrationConfig", "orchestrationGraph"]) {
1029
+ if (row[field] !== undefined && row[field] !== null && row[field] !== "") {
1030
+ if (typeof row[field] !== "string" && (typeof row[field] !== "object" || Array.isArray(row[field]))) {
1031
+ errors.push(`${path}.${field} must be a JSON string or plain object`);
1032
+ }
1022
1033
  }
1023
1034
  }
1024
1035
  if (row.envRefs !== undefined && typeof row.envRefs !== "string" && !Array.isArray(row.envRefs)) {
@@ -1115,6 +1126,88 @@ function validateSandboxEnvironmentRow(row, path, errors) {
1115
1126
  }
1116
1127
  }
1117
1128
 
1129
+ /**
1130
+ * Validate the OPTIONAL Nango binding fields that may appear on an
1131
+ * `objectType: "api-registry"` row when `connectorKind === "nango"`.
1132
+ *
1133
+ * The canonical api-registry columns (`integrationId`, `authRef`, `baseUrl`,
1134
+ * `endpoint`, `method`, `status`, `connectorKind`, `resolverTemplateId`, ...)
1135
+ * are owned by `lib/workspace-data-model.js` and validated by their own
1136
+ * shape. This function only inspects the Nango extension fields and is a
1137
+ * no-op for HTTP / MCP / Chrome / tool / custom rows.
1138
+ */
1139
+ function validateApiRegistryRow(row, path, errors) {
1140
+ if (!isPlainObject(row)) return;
1141
+ // Defensive: any api-registry row is forbidden from persisting raw
1142
+ // credentials. The auth ref lives in `authRef` (an env-ref name) and the
1143
+ // actual secret stays in env. This guard applies to ALL connectorKinds.
1144
+ const FORBIDDEN_TOKEN_FIELDS = [
1145
+ "secret",
1146
+ "secretKey",
1147
+ "apiKey",
1148
+ "token",
1149
+ "authToken",
1150
+ "accessToken",
1151
+ "refreshToken",
1152
+ "bearer",
1153
+ "password"
1154
+ ];
1155
+ for (const forbidden of FORBIDDEN_TOKEN_FIELDS) {
1156
+ if (Object.prototype.hasOwnProperty.call(row, forbidden)) {
1157
+ errors.push(`${path}.${forbidden} is not allowed on an api-registry row — store the env-ref name in authRef and keep the secret in env`);
1158
+ }
1159
+ }
1160
+ if (row.connectorKind !== "nango") return;
1161
+ // Nango-specific binding fields. All are optional individually, but
1162
+ // `providerConfigKey` must resolve at runtime — either explicitly or by
1163
+ // falling back to `integrationId`. Validate format only when present.
1164
+ if (row.providerConfigKey !== undefined && row.providerConfigKey !== null && row.providerConfigKey !== "") {
1165
+ if (
1166
+ typeof row.providerConfigKey !== "string"
1167
+ || row.providerConfigKey.length > NANGO_PROVIDER_CONFIG_KEY_MAX
1168
+ || !NANGO_PROVIDER_CONFIG_KEY_PATTERN.test(row.providerConfigKey)
1169
+ ) {
1170
+ errors.push(`${path}.providerConfigKey must be alphanumeric (with _.- separators), starting alphanumeric, and <= ${NANGO_PROVIDER_CONFIG_KEY_MAX} chars`);
1171
+ }
1172
+ }
1173
+ if (row.nangoMode !== undefined && row.nangoMode !== "" && !KNOWN_NANGO_MODES.includes(row.nangoMode)) {
1174
+ errors.push(`${path}.nangoMode must be one of ${KNOWN_NANGO_MODES.join(", ")}`);
1175
+ }
1176
+ if (row.nangoHostUrl !== undefined && row.nangoHostUrl !== null && row.nangoHostUrl !== "" && typeof row.nangoHostUrl !== "string") {
1177
+ errors.push(`${path}.nangoHostUrl must be a string when present`);
1178
+ }
1179
+ if (row.nangoEnvironment !== undefined && row.nangoEnvironment !== null && row.nangoEnvironment !== "" && typeof row.nangoEnvironment !== "string") {
1180
+ errors.push(`${path}.nangoEnvironment must be a string when present`);
1181
+ }
1182
+ if (row.connectionIds !== undefined) {
1183
+ if (typeof row.connectionIds === "string") {
1184
+ // Allow comma-separated form for hand-edits — splitting/normalization
1185
+ // happens at the resolver boundary, not in the validator.
1186
+ } else if (!Array.isArray(row.connectionIds)) {
1187
+ errors.push(`${path}.connectionIds must be a comma-separated string or an array of strings`);
1188
+ } else {
1189
+ row.connectionIds.forEach((value, index) => {
1190
+ if (typeof value !== "string" || !value.trim()) {
1191
+ errors.push(`${path}.connectionIds[${index}] must be a non-empty string`);
1192
+ }
1193
+ });
1194
+ }
1195
+ }
1196
+ if (row.enabledActions !== undefined) {
1197
+ if (typeof row.enabledActions === "string") {
1198
+ // Comma-separated form is allowed.
1199
+ } else if (!Array.isArray(row.enabledActions)) {
1200
+ errors.push(`${path}.enabledActions must be a comma-separated string or an array of strings`);
1201
+ } else {
1202
+ row.enabledActions.forEach((value, index) => {
1203
+ if (typeof value !== "string" || !value.trim()) {
1204
+ errors.push(`${path}.enabledActions[${index}] must be a non-empty string`);
1205
+ }
1206
+ });
1207
+ }
1208
+ }
1209
+ }
1210
+
1118
1211
  const NAV_FOLDERS_OBJECT_ID = "nav-folders";
1119
1212
  const NAV_FOLDER_NAME_MAX = 60;
1120
1213
  const NAV_ITEM_LABEL_MAX = 80;
@@ -1265,6 +1358,9 @@ function validateDataModelConfig(dataModel, errors) {
1265
1358
  if (object.objectType === "sandbox-environment") {
1266
1359
  validateSandboxEnvironmentRow(row, `${prefix}.rows[${rowIndex}]`, errors);
1267
1360
  }
1361
+ if (object.objectType === "api-registry") {
1362
+ validateApiRegistryRow(row, `${prefix}.rows[${rowIndex}]`, errors);
1363
+ }
1268
1364
  if (object.id === NAV_FOLDERS_OBJECT_ID) {
1269
1365
  validateNavFolderRow(row, `${prefix}.rows[${rowIndex}]`, errors);
1270
1366
  }
@@ -1536,11 +1632,14 @@ export {
1536
1632
  KNOWN_FIELDS,
1537
1633
  KNOWN_FILTER_CONJUNCTIONS,
1538
1634
  KNOWN_FILTER_OPERATORS,
1635
+ KNOWN_NANGO_MODES,
1539
1636
  KNOWN_SANDBOX_AGENT_HOSTS,
1540
1637
  KNOWN_SANDBOX_RUN_LOCALITY,
1541
1638
  KNOWN_SANDBOX_RUNTIMES,
1542
1639
  KNOWN_SORT_DIRECTIONS,
1543
1640
  KNOWN_WIDGET_KINDS,
1641
+ NANGO_PROVIDER_CONFIG_KEY_MAX,
1642
+ NANGO_PROVIDER_CONFIG_KEY_PATTERN,
1544
1643
  DEFAULT_SANDBOX_ADAPTER,
1545
1644
  SANDBOX_DEFAULT_TIMEOUT_MS,
1546
1645
  SANDBOX_MAX_TIMEOUT_MS,
@@ -10,6 +10,7 @@
10
10
  "lint": "next lint"
11
11
  },
12
12
  "dependencies": {
13
+ "@nangohq/node": "^0.70.4",
13
14
  "@tanstack/react-table": "^8.21.3",
14
15
  "lucide-react": "^0.468.0",
15
16
  "next": "^16.2.6",
@@ -40,6 +40,7 @@
40
40
  "templates/deployment-plan.md",
41
41
  "templates/project.md",
42
42
  "templates/self-eval.md",
43
+ "templates/seeded-configs/project-management.config.json",
43
44
  "helpers/README.md",
44
45
  "skills/README.md",
45
46
  "examples/workspace-sample.md",
@@ -44,6 +44,7 @@
44
44
  "templates/project.md",
45
45
  "templates/self-eval.md",
46
46
  "templates/seeded-configs/alignment-loop.config.json",
47
+ "templates/seeded-configs/project-management.config.json",
47
48
  "helpers/README.md",
48
49
  "helpers/propose-capability.mjs",
49
50
  "helpers/promote-capability.mjs",
@@ -169,6 +170,7 @@
169
170
  "templates/deployment-handoff.md",
170
171
  "templates/supabase-setup-plan.md",
171
172
  "templates/seeded-configs/alignment-loop.config.json",
173
+ "templates/seeded-configs/project-management.config.json",
172
174
  "helpers",
173
175
  "helpers/README.md",
174
176
  "helpers/propose-capability.mjs",
@@ -0,0 +1,276 @@
1
+ {
2
+ "name": "Project Management Workspace Template",
3
+ "description": "Opinionated project-management workspace template for API-backed task workflows. This seed is sanitized: it stores no provider secrets, no OAuth connection ids, and no user task data.",
4
+ "dataModel": {
5
+ "objects": [
6
+ {
7
+ "id": "api-registry",
8
+ "label": "API Registry",
9
+ "source": "API Registry",
10
+ "objectType": "api-registry",
11
+ "icon": "Code2",
12
+ "locked": true,
13
+ "columns": [
14
+ "integrationId",
15
+ "authRef",
16
+ "baseUrl",
17
+ "endpoint",
18
+ "method",
19
+ "status",
20
+ "lastTested",
21
+ "lastResponse",
22
+ "entityTypes",
23
+ "description",
24
+ "connectorKind",
25
+ "resolverTemplateId",
26
+ "schemaVersion",
27
+ "capabilities",
28
+ "executionLane",
29
+ "providerConfigKey",
30
+ "connectionIds",
31
+ "nangoMode",
32
+ "nangoEnvironment"
33
+ ],
34
+ "records": [
35
+ {
36
+ "integrationId": "asana-active-tasks",
37
+ "authRef": "NANGO_SECRET_KEY",
38
+ "baseUrl": "http://127.0.0.1:3000",
39
+ "endpoint": "/api/workspace/integrations/nango/proxy",
40
+ "method": "POST",
41
+ "status": "template",
42
+ "lastTested": "",
43
+ "lastResponse": "",
44
+ "entityTypes": "tasks",
45
+ "description": "Template API Registry row for pulling active project tasks through a provider-auth proxy. Set providerConfigKey and connectionIds in the exported workspace after OAuth.",
46
+ "connectorKind": "nango",
47
+ "resolverTemplateId": "nango-proxy-v1",
48
+ "schemaVersion": "1",
49
+ "capabilities": "read:tasks",
50
+ "executionLane": "project-management",
51
+ "providerConfigKey": "asana",
52
+ "connectionIds": "",
53
+ "nangoMode": "cloud",
54
+ "nangoEnvironment": "dev"
55
+ }
56
+ ],
57
+ "binding": {
58
+ "mode": "manual",
59
+ "source": "Data Model"
60
+ },
61
+ "fieldSettings": {
62
+ "hidden": [
63
+ "lastResponse"
64
+ ]
65
+ }
66
+ },
67
+ {
68
+ "id": "project-task-source",
69
+ "label": "Project Task Source",
70
+ "source": "Project Task Source",
71
+ "objectType": "data-source",
72
+ "icon": "Globe",
73
+ "locked": true,
74
+ "columns": [
75
+ "Name",
76
+ "registryId",
77
+ "endpoint",
78
+ "authRef",
79
+ "baseUrl",
80
+ "method",
81
+ "status",
82
+ "lastTested",
83
+ "lastResponse",
84
+ "sourceStorage",
85
+ "sourceId",
86
+ "entityType",
87
+ "description"
88
+ ],
89
+ "records": [
90
+ {
91
+ "Name": "Active project tasks",
92
+ "registryId": "asana-active-tasks",
93
+ "endpoint": "/api/workspace/integrations/nango/proxy",
94
+ "authRef": "NANGO_SECRET_KEY",
95
+ "baseUrl": "http://127.0.0.1:3000",
96
+ "method": "POST",
97
+ "status": "template",
98
+ "lastTested": "",
99
+ "lastResponse": "",
100
+ "sourceStorage": "workspace-source-records",
101
+ "sourceId": "project-active-tasks",
102
+ "entityType": "tasks",
103
+ "description": "Source binding for task deltas returned by the project-management workflow."
104
+ }
105
+ ],
106
+ "binding": {
107
+ "mode": "manual",
108
+ "source": "Data Model"
109
+ },
110
+ "fieldSettings": {
111
+ "hidden": [
112
+ "lastResponse"
113
+ ]
114
+ }
115
+ },
116
+ {
117
+ "id": "sandbox-environments",
118
+ "label": "Sandbox Environments",
119
+ "source": "Sandbox Environments",
120
+ "objectType": "sandbox-environment",
121
+ "icon": "Terminal",
122
+ "locked": true,
123
+ "columns": [
124
+ "Name",
125
+ "lifecycleStatus",
126
+ "version",
127
+ "runLocality",
128
+ "schedulerRegistryId",
129
+ "runtime",
130
+ "adapter",
131
+ "agentHost",
132
+ "localModel",
133
+ "localEndpoint",
134
+ "intelligenceAdapterMode",
135
+ "envRefs",
136
+ "networkAllow",
137
+ "allowList",
138
+ "instructions",
139
+ "command",
140
+ "timeoutMs",
141
+ "status",
142
+ "lastTested",
143
+ "lastRunId",
144
+ "lastSourceId",
145
+ "lastResponse",
146
+ "orchestrationConfig",
147
+ "description"
148
+ ],
149
+ "records": [
150
+ {
151
+ "Name": "project-active-tasks-workflow",
152
+ "lifecycleStatus": "draft",
153
+ "version": "1",
154
+ "runLocality": "local",
155
+ "schedulerRegistryId": "",
156
+ "runtime": "node",
157
+ "adapter": "local-process",
158
+ "agentHost": "",
159
+ "localModel": "",
160
+ "localEndpoint": "",
161
+ "intelligenceAdapterMode": "",
162
+ "envRefs": "NANGO_SECRET_KEY",
163
+ "networkAllow": "true",
164
+ "allowList": "127.0.0.1,localhost,api.nango.dev,app.asana.com",
165
+ "instructions": "Template workflow for pulling active tasks from a project-management provider through API Registry. Fill projectGid, workspaceGid, providerConfigKey, and connectionIds in the exported workspace before running.",
166
+ "command": "",
167
+ "timeoutMs": "60000",
168
+ "status": "template",
169
+ "lastTested": "",
170
+ "lastRunId": "",
171
+ "lastSourceId": "",
172
+ "lastResponse": "",
173
+ "orchestrationConfig": "{\n \"version\": 1,\n \"provider\": \"growthub-native\",\n \"nodes\": [\n {\n \"id\": \"input\",\n \"type\": \"input\",\n \"label\": \"Project input\",\n \"config\": {\n \"inputMode\": \"manual\",\n \"samplePayload\": {\n \"projectGid\": \"PROJECT_GID_PLACEHOLDER\",\n \"workspaceGid\": \"WORKSPACE_GID_PLACEHOLDER\"\n }\n }\n },\n {\n \"id\": \"active-tasks-call\",\n \"type\": \"api-registry-call\",\n \"label\": \"Pull active project tasks\",\n \"config\": {\n \"registryId\": \"asana-active-tasks\",\n \"integrationId\": \"asana-active-tasks\",\n \"method\": \"POST\",\n \"baseUrl\": \"http://127.0.0.1:3000\",\n \"endpoint\": \"/api/workspace/integrations/nango/proxy\",\n \"authRef\": \"NANGO_SECRET_KEY\",\n \"bodyTemplate\": {\n \"providerConfigKey\": \"asana\",\n \"connectionId\": \"CONNECTION_ID_PLACEHOLDER\",\n \"method\": \"GET\",\n \"path\": \"/api/1.0/projects/{{input.projectGid}}/tasks\",\n \"params\": {\n \"completed_since\": \"now\",\n \"limit\": \"20\",\n \"opt_fields\": \"gid,name,completed,assignee.name,due_on,permalink_url,created_at,modified_at,memberships.section.name\"\n }\n },\n \"deltaTags\": [\n \"routing\",\n \"input\",\n \"output\"\n ]\n }\n },\n {\n \"id\": \"normalize-active-tasks\",\n \"type\": \"transform-filter\",\n \"label\": \"Normalize active tasks\",\n \"config\": {\n \"rootPath\": \"data\",\n \"filter\": {\n \"completed\": false\n },\n \"fieldMap\": {\n \"gid\": \"gid\",\n \"name\": \"name\",\n \"completed\": \"completed\",\n \"assignee\": \"assignee.name\",\n \"due_on\": \"due_on\",\n \"section\": \"memberships.0.section.name\",\n \"permalink_url\": \"permalink_url\",\n \"modified_at\": \"modified_at\"\n },\n \"deltaTags\": [\n \"evaluation\",\n \"output\"\n ]\n }\n },\n {\n \"id\": \"result\",\n \"type\": \"tool-result\",\n \"label\": \"Persist task response\",\n \"config\": {\n \"saveStatus\": true,\n \"saveResponse\": true,\n \"sourceId\": \"project-active-tasks\",\n \"deltaTags\": [\n \"output\"\n ]\n }\n }\n ],\n \"edges\": [\n {\n \"from\": \"input\",\n \"to\": \"active-tasks-call\",\n \"passes\": \"input\"\n },\n {\n \"from\": \"active-tasks-call\",\n \"to\": \"normalize-active-tasks\",\n \"passes\": \"response\"\n },\n {\n \"from\": \"normalize-active-tasks\",\n \"to\": \"result\",\n \"passes\": \"normalized-output\"\n }\n ]\n}",
174
+ "description": "Draft project-management workflow template. No secrets or provider data are stored in this row."
175
+ }
176
+ ],
177
+ "binding": {
178
+ "mode": "manual",
179
+ "source": "Data Model"
180
+ },
181
+ "fieldSettings": {
182
+ "hidden": [
183
+ "lastResponse"
184
+ ]
185
+ }
186
+ }
187
+ ]
188
+ },
189
+ "dashboards": [
190
+ {
191
+ "id": "project-management-template",
192
+ "name": "Project Management",
193
+ "description": "Template dashboard for active project tasks. Run the workflow after configuring provider OAuth to hydrate real rows.",
194
+ "tabs": [
195
+ {
196
+ "id": "tab-project-tasks",
197
+ "label": "Active Tasks",
198
+ "widgets": [
199
+ {
200
+ "id": "project-active-tasks-view",
201
+ "kind": "view",
202
+ "title": "Active project tasks",
203
+ "position": {
204
+ "x": 0,
205
+ "y": 0,
206
+ "w": 9,
207
+ "h": 8
208
+ },
209
+ "config": {
210
+ "source": "Project Task Source",
211
+ "layout": "Table",
212
+ "columns": [
213
+ "gid",
214
+ "name",
215
+ "assignee",
216
+ "due_on",
217
+ "section",
218
+ "modified_at",
219
+ "permalink_url"
220
+ ],
221
+ "rows": []
222
+ }
223
+ }
224
+ ]
225
+ }
226
+ ],
227
+ "activeTabId": "tab-project-tasks",
228
+ "version": "1"
229
+ }
230
+ ],
231
+ "canvas": {
232
+ "id": "workspace-canvas",
233
+ "name": "Project Management",
234
+ "scope": "workspace",
235
+ "layout": {
236
+ "columns": 12,
237
+ "rowHeight": 64,
238
+ "gap": 16,
239
+ "responsive": true
240
+ },
241
+ "widgets": [
242
+ {
243
+ "id": "project-active-tasks-view",
244
+ "kind": "view",
245
+ "title": "Active project tasks",
246
+ "position": {
247
+ "x": 0,
248
+ "y": 0,
249
+ "w": 9,
250
+ "h": 8
251
+ },
252
+ "config": {
253
+ "source": "Project Task Source",
254
+ "layout": "Table",
255
+ "columns": [
256
+ "gid",
257
+ "name",
258
+ "assignee",
259
+ "due_on",
260
+ "section",
261
+ "modified_at",
262
+ "permalink_url"
263
+ ],
264
+ "rows": []
265
+ }
266
+ }
267
+ ]
268
+ },
269
+ "provenance": {
270
+ "mirrors": "growthub-custom-workspace-starter-v1",
271
+ "template": "project-management",
272
+ "templateKind": "workspace-template",
273
+ "privacy": "sanitized-no-secrets-no-provider-data",
274
+ "note": "Official Workspace 2 seed. Configure provider OAuth and project identifiers after export."
275
+ }
276
+ }